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 ++ 278 files changed, 91117 insertions(+), 13 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 (limited to '3rdparty') 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 */ -- cgit v1.2.3-55-g7522