summaryrefslogtreecommitdiffstats
path: root/3rdparty
diff options
context:
space:
mode:
authorSebastien Braun2010-07-15 01:12:17 +0200
committerSebastien Braun2010-07-15 01:12:17 +0200
commit94b88e75b9ebeaf9abb2adef130fdf971884e7b4 (patch)
treecc0bb545880b0d159267290d7aa5c63693905dcb /3rdparty
parentChange to shorter NAK intervals for performance. (diff)
downloadpvs-94b88e75b9ebeaf9abb2adef130fdf971884e7b4.tar.gz
pvs-94b88e75b9ebeaf9abb2adef130fdf971884e7b4.tar.xz
pvs-94b88e75b9ebeaf9abb2adef130fdf971884e7b4.zip
* Upgrade OpenPGM to current trunk
* Implement wait-for-shutdown for McastPGMSocket * Work around bug in UDP encapsulation
Diffstat (limited to '3rdparty')
-rw-r--r--3rdparty/CMakeLists.txt19
-rw-r--r--3rdparty/libpgm-5.0.63alpha1.tar.bz2bin358887 -> 0 bytes
-rw-r--r--3rdparty/openpgm-svn-r1085/doc/draft-ietf-rmt-bb-pgmcc-03.txt1226
-rw-r--r--3rdparty/openpgm-svn-r1085/doc/rfc3208.txt6219
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/COPYING504
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/INSTALL4
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/LICENSE19
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/README7
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm170
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgm8980
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmex18
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmhttp53
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConscript.libpgmsnmp35
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct326
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.097321
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.intelc331
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.mingw64325
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.097.sunstudio312
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.Debian4281
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.FreeBSD80337
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.OpenSolaris310
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.RHEL4279
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.gcc64324
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sungcc321
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.Solaris.sunstudio306
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.clang336
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw330
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/SConstruct.mingw-wine339
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/atomic_unittest.c166
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/backtrace.c69
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/checksum.c941
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/checksum_perftest.c807
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/checksum_unittest.c278
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/crossmingw.py144
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/crossmingw64.py140
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/engine.c277
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/engine_unittest.c232
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/error.c518
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/error_unittest.c292
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/SConscript88
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/SConscript8941
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/async.c441
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/async.h82
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/blocksyncrecv.c350
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/daytime.c546
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecv.c382
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsg.c382
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/enonblocksyncrecvmsgv.c397
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/getopt.c110
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/getopt.h62
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/pgmdump.c279
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/pgmping.cc1059
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/pgmrecv.c649
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/pgmsend.c305
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c1031
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/ping.proto47
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/pnonblocksyncrecv.c385
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/purinrecv.c474
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/purinrecvcc.cc434
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/purinsend.c280
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/purinsendcc.cc269
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/shortcakerecv.c430
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/snonblocksyncrecv.c435
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/fec-block.txt66
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/fec.txt77
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/galois_generator.pl139
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/gcov-parse.pl41
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/gcov-seed.sh33
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/gcov.sh29
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/getifaddrs.c840
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/getifaddrs_unittest.c262
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/getnodeaddr.c196
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/getnodeaddr_unittest.c573
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/gsi.c227
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/gsi_unittest.c350
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/hashtable.c327
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/histogram.c414
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/htdocs/404.html11
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/htdocs/base.css136
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/htdocs/convert_to_macro.pl20
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/htdocs/robots.txt2
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/htdocs/xhtml10_strict.doctype3
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/http.c1718
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/http_unittest.c186
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/if.c1595
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/if_unittest.c1495
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/checksum.h75
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/engine.h43
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/features.h42
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/fixed.h140
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/framework.h76
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/galois.h138
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/getifaddrs.h73
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/getnodeaddr.h41
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/hashtable.h58
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/histogram.h129
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/i18n.h32
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoaddr.h41
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/indextoname.h37
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/inet_network.h41
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/ip.h150
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/list.h43
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/math.h75
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/md5.h61
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/mem.h34
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/messages.h352
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/nametoindex.h40
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/net.h38
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/notify.h298
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_parse.h45
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/packet_test.h40
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB.h28
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_columns.h372
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/pgmMIB_enums.h64
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/processor.h61
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/queue.h51
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/rand.h50
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/rate_control.h54
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/receiver.h142
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/reed_solomon.h51
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/rxw.h220
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/slist.h52
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/sn.h186
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/sockaddr.h105
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/socket.h182
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/source.h75
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/sqn_list.h38
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/string.h59
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/thread.h210
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/time.h51
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/timer.h58
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/tsi.h39
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/txw.h204
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/impl/wsastrerror.h37
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/atomic.h140
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/backtrace.h33
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/engine.h37
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/error.h109
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/gsi.h49
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/http.h37
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/if.h33
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm.hh100
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/ip/pgm_endpoint.hh171
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/list.h40
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/log.h33
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/macros.h171
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/mem.h60
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/messages.h66
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/msgv.h54
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/packet.h472
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.h42
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm.hh33
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/pgm_socket.hh157
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/signal.h36
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/skbuff.h243
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/snmp.h35
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/socket.h170
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/time.h54
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/tsi.h47
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/types.h56
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/version.h41
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/winint.h198
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/include/pgm/wininttypes.h254
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/indextoaddr.c98
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/indextoaddr_unittest.c302
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/indextoname.c52
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/inet_network.c237
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/inet_network_unittest.c203
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/ip_unittest.c362
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/list.c146
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/log.c151
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/math.c75
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/md5.c368
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/md5_unittest.c189
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/mem.c249
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/memcheck13
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/messages.c173
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/mibs/PGM-MIB-petrova-01.txt5459
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/mld-semantics.txt52
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/msfec.txt33
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/nametoindex.c249
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/net-snmp.txt34
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/net.c175
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/net_unittest.c375
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/options.txt158
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/packet_parse.c615
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/packet_parse_unittest.c382
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/packet_test.c1158
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/packet_test_unittest.c169
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/pgmMIB.c3212
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/pgmMIB_unittest.c257
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/plan.txt238
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/queue.c110
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/rand.c137
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/rate_control.c158
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/rate_control_unittest.c241
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/receiver.c2268
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/receiver_unittest.c857
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/recv.c1059
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/recv_unittest.c1600
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/reed_solomon.c576
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/reed_solomon_unittest.c305
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/rxw.c2229
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/rxw_unittest.c1844
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/signal.c176
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/signal_unittest.c115
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/skbuff.c115
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/slist.c166
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/snmp.c222
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/snmp_unittest.c184
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/sockaddr.c1193
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/socket.c2046
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/socket_unittest.c1186
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/source.c2339
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/source_unittest.c1216
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/string.c486
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/test/PGM/Test.pm394
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/test/SConscript15
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/ambient_spm.pl86
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/apdu.pl56
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/apdu_parity.pl59
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/test/app.c904
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/test/async.c572
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/test/async.h76
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/test/dump-json.c1292
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/test/dump-json.h33
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/heartbeat_spm.pl57
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/test/monitor.c349
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/nak.pl66
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/nak_cancellation.pl161
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/nak_list.pl70
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/nak_parity.pl75
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/nak_repeat.pl69
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/ncf.pl66
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/ncf_cancellation.pl147
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/ncf_list.pl72
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/ncf_suppression.pl101
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/odata.pl44
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/odata_completion.pl87
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/odata_jump.pl71
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/odata_jump_parity.pl83
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/odata_number.pl59
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/odata_rate.pl68
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/odata_reception.pl59
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/on-demand_spm.pl49
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/outofwindow_ncf.pl103
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion.pl87
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity.pl97
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/rdata_completion_parity_var_pktlen.pl97
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/rdata_jump.pl71
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/rdata_reception.pl59
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/test/sim.c1924
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/spm.pl42
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/spm_jump.pl60
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/spm_jump2.pl59
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/spm_reception.pl58
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/spmr.pl73
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/spmr_after_spm.pl78
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/spmr_from_odata.pl53
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/test/spmr_suppression.pl58
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/test/sudoers.example26
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/test/test.conf.pl27
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/thread.c457
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/time.c770
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/time_unittest.c188
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/timer.c223
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/timer_unittest.c355
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/token and leaky bucket.txt12
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/tsi.c119
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/tsi_unittest.c185
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/txw.c763
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/txw_unittest.c743
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/valgrind.supp147
-rwxr-xr-x3rdparty/openpgm-svn-r1085/pgm/version_generator.py52
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.13-1openpgm3.diff136
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/win/mingw32-runtime_3.15.2-0openpgm1.diff135
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/win64/mingw-w64-bin_x86-64-linux_4.4.1-1openpgm1.diff53
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/wsastrerror.c372
278 files changed, 91117 insertions, 13 deletions
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
--- a/3rdparty/libpgm-5.0.63alpha1.tar.bz2
+++ /dev/null
Binary files 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 {
+ <update acker statistics (p_ACKER, RTT_ACKER) >
+ 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 ( <there are unacked packet> )
+ <set a timeout, see Sec.2.7 >
+}
+
+
+
+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 ( <some data packet is missing and unacknowledged > )
+ < 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 ( <some data packet is missing and unacknowledged > )
+ < 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should 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.
+
+ <signature of Ty Coon>, 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 <sys/eventfd.h>
+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 <sys/eventfd.h>
+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 <sys/eventfd.h>
+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 <sys/eventfd.h>
+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 <sys/eventfd.h>
+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 <sys/eventfd.h>
+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 <sys/eventfd.h>
+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 <sys/eventfd.h>
+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 <sys/eventfd.h>
+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 <sys/eventfd.h>
+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 <sys/eventfd.h>
+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 <stdint.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <stdio.h>
+# include <stdlib.h>
+# include <execinfo.h>
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+#include <glib.h>
+#include <pgm/backtrace.h>
+
+
+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 <impl/framework.h>
+
+
+/* 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 <inttypes.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <netdb.h>
+#endif
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/engine.h>
+#include <impl/mem.h>
+#include <impl/socket.h>
+#include <pgm/engine.h>
+#include <pgm/version.h>
+
+
+//#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 <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <netdb.h>
+#endif
+#include <impl/i18n.h>
+#include <impl/framework.h>
+
+
+//#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 <stdint.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#ifndef _WIN32
+# include <fcntl.h>
+# include <unistd.h>
+# include <pthread.h>
+#else
+# include <process.h>
+#endif
+#include <pgm/pgm.h>
+
+#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 <errno.h>
+#include <pgm/pgm.h>
+
+#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 <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#ifndef G_OS_WIN32
+# include <unistd.h>
+#else
+# include "getopt.h"
+#endif
+#include <pgm/pgm.h>
+
+/* example dependencies */
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+
+
+/* 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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : 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 <assert.h>
+#include <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#ifndef _WIN32
+# include <fcntl.h>
+# include <unistd.h>
+# include <pthread.h>
+#else
+# include <process.h>
+# include <wchar.h>
+# include "getopt.h"
+# define snprintf _snprintf
+#endif
+#include <pgm/pgm.h>
+
+
+/* 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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : Encapsulate PGM in UDP on IP port\n");
+ fprintf (stderr, " -r <rate> : Regulate to rate bytes per second\n");
+ fprintf (stderr, " -c : Enable PGMCC\n");
+ fprintf (stderr, " -f <type> : Enable FEC: proactive, ondemand, or both\n");
+ fprintf (stderr, " -N <n> : Reed-Solomon block size (255)\n");
+ fprintf (stderr, " -K <k> : Reed-Solomon group size (8)\n");
+ fprintf (stderr, " -P <count> : 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 <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <glib.h>
+#ifdef G_OS_UNIX
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <netinet/in.h>
+# include <sys/socket.h>
+#endif
+#include <pgm/pgm.h>
+
+/* example dependencies */
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+
+
+/* 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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : 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 <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <glib.h>
+#ifdef G_OS_UNIX
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <netinet/in.h>
+# include <sys/socket.h>
+#endif
+#include <pgm/pgm.h>
+
+/* example dependencies */
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+
+
+/* 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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : 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 <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <glib.h>
+#ifdef G_OS_UNIX
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <netinet/in.h>
+# include <sys/socket.h>
+# include <sys/uio.h>
+#endif
+#include <pgm/pgm.h>
+
+/* example dependencies */
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+
+
+/* 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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : 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 <stdio.h>
+#include <string.h>
+#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 <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <glib.h>
+#ifdef G_OS_UNIX
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <netinet/in.h>
+# include <sys/socket.h>
+#endif
+
+#include <pgm/pgm.h>
+
+/* PGM internals */
+#include <impl/packet_test.h>
+
+/* example dependencies */
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+
+
+/* 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 <cerrno>
+#include <clocale>
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <inttypes.h>
+#include <math.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#ifdef CONFIG_HAVE_EPOLL
+# include <sys/epoll.h>
+#endif
+#include <sys/types.h>
+#ifndef _WIN32
+# include <netdb.h>
+# include <netinet/in.h>
+# include <sched.h>
+# include <sys/socket.h>
+# include <arpa/inet.h>
+#endif
+#include <glib.h>
+#include <pgm/pgm.h>
+#ifdef CONFIG_WITH_HTTP
+# include <pgm/http.h>
+#endif
+#ifdef CONFIG_WITH_SNMP
+# include <pgm/snmp.h>
+#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 <pgm/backtrace.h>
+#include <pgm/log.h>
+#include <pgm/signal.h>
+
+
+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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : Encapsulate PGM in UDP on IP port\n");
+ fprintf (stderr, " -d <seconds> : Terminate transport after duration.\n");
+ fprintf (stderr, " -m <frequency> : 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 <rate> : Regulate to rate bytes per second\n");
+ fprintf (stderr, " -c : Enable PGMCC\n");
+ fprintf (stderr, " -f <type> : Enable FEC with either proactive or ondemand parity\n");
+ fprintf (stderr, " -K <k> : Configure Reed-Solomon code (n, k)\n");
+ fprintf (stderr, " -N <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 <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef CONFIG_HAVE_EPOLL
+# include <sys/epoll.h>
+#endif
+#include <sys/types.h>
+#include <glib.h>
+#ifdef G_OS_UNIX
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <netinet/in.h>
+# include <unistd.h>
+# include <sys/socket.h>
+# include <sys/uio.h>
+# include <sys/time.h>
+#else
+# include "getopt.h"
+#endif
+#include <pgm/pgm.h>
+#ifdef CONFIG_WITH_HTTP
+# include <pgm/http.h>
+#endif
+#ifdef CONFIG_WITH_SNMP
+# include <pgm/snmp.h>
+#endif
+
+/* example dependencies */
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+#include <pgm/signal.h>
+
+
+/* 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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -a <ip address> : Source unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : 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 <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <glib.h>
+#ifdef G_OS_UNIX
+# include <netdb.h>
+# include <unistd.h>
+# include <arpa/inet.h>
+# include <netinet/in.h>
+# include <sys/socket.h>
+# include <sys/time.h>
+#else
+# include "getopt.h"
+#endif
+#include <pgm/pgm.h>
+
+/* example dependencies */
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+
+
+/* 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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : Encapsulate PGM in UDP on IP port\n");
+ fprintf (stderr, " -r <rate> : Regulate to rate bytes per second\n");
+ fprintf (stderr, " -f <type> : Enable FEC with either proactive or ondemand parity\n");
+ fprintf (stderr, " -K <k> : Configure Reed-Solomon code (n, k)\n");
+ fprintf (stderr, " -N <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 <errno.h>
+#include <locale.h>
+#include <ncurses.h>
+#include <netdb.h>
+#include <panel.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include <pgm/pgm.h>
+
+/* PGM internals */
+#include <pgm/packet.h>
+
+/* example dependencies */
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+
+
+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 <errno.h>
+#include <locale.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <glib.h>
+#ifdef G_OS_UNIX
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <netinet/in.h>
+# include <sys/socket.h>
+#endif
+#include <pgm/pgm.h>
+
+/* example dependencies */
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+
+
+/* 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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : 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 <assert.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef _WIN32
+# include <unistd.h>
+#else
+# include "getopt.h"
+# define snprintf _snprintf
+#endif
+#include <pgm/pgm.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_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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : Encapsulate PGM in UDP on IP port\n");
+ fprintf (stderr, " -c : Enable PGMCC\n");
+ fprintf (stderr, " -f <type> : Enable FEC with either proactive or ondemand parity\n");
+ fprintf (stderr, " -K <k> : Configure Reed-Solomon code (n, k)\n");
+ fprintf (stderr, " -N <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 <cassert>
+#include <clocale>
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#ifndef _WIN32
+# include <unistd.h>
+# include <netinet/in.h>
+# include <sys/socket.h>
+#else
+# include "getopt.h"
+#endif
+#include <pgm/pgm.hh>
+
+
+/* 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 <network> : Multicast group or unicast IP address" << std::endl;
+ std::cerr << " -s <port> : IP port" << std::endl;
+ std::cerr << " -p <port> : Encapsulate PGM in UDP on IP port" << std::endl;
+ std::cerr << " -f <type> : Enable FEC with either proactive or ondemand parity" << std::endl;
+ std::cerr << " -K <k> : Configure Reed-Solomon code (n, k)" << std::endl;
+ std::cerr << " -N <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 <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef _WIN32
+# include <unistd.h>
+#else
+# include "getopt.h"
+# define snprintf _snprintf
+#endif
+#include <pgm/pgm.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 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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : Encapsulate PGM in UDP on IP port\n");
+ fprintf (stderr, " -r <rate> : Regulate to rate bytes per second\n");
+ fprintf (stderr, " -f <type> : Enable FEC with either proactive or ondemand parity\n");
+ fprintf (stderr, " -K <k> : Configure Reed-Solomon code (n, k)\n");
+ fprintf (stderr, " -N <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 <clocale>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#ifndef _WIN32
+# include <unistd.h>
+# include <netinet/in.h>
+#else
+# include "getopt.h"
+#endif
+#include <pgm/pgm.hh>
+
+
+/* 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 <network> : Multicast group or unicast IP address" << std::endl;
+ std::cerr << " -s <port> : IP port" << std::endl;
+ std::cerr << " -p <port> : Encapsulate PGM in UDP on IP port" << std::endl;
+ std::cerr << " -r <rate> : Regulate to rate bytes per second" << std::endl;
+ std::cerr << " -f <type> : Enable FEC with either proactive or ondemand parity" << std::endl;
+ std::cerr << " -K <k> : Configure Reed-Solomon code (n, k)" << std::endl;
+ std::cerr << " -N <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 <assert.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef _WIN32
+# include <unistd.h>
+#else
+# include "getopt.h"
+# define snprintf _snprintf
+#endif
+#include <pgm/pgm.h>
+
+#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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : Encapsulate PGM in UDP on IP port\n");
+ fprintf (stderr, " -f <type> : Enable FEC with either proactive or ondemand parity\n");
+ fprintf (stderr, " -K <k> : Configure Reed-Solomon code (n, k)\n");
+ fprintf (stderr, " -N <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 <errno.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <glib.h>
+#ifdef G_OS_UNIX
+# include <netdb.h>
+# include <arpa/inet.h>
+# include <netinet/in.h>
+# include <sys/socket.h>
+#endif
+#include <pgm/pgm.h>
+
+/* example dependencies */
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+
+
+/* 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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : IP port\n");
+ fprintf (stderr, " -p <port> : 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<<MOO;
+/* vim:ts=8:sts=8:sw=4:noai:noexpandtab
+ *
+ * Galois field tables
+ *
+ * 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 <impl/framework.h>
+
+
+/* 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<<MOO;
+};
+
+const pgm_gf8_t pgm_gfantilog[PGM_GF_NO_ELEMENTS] =
+{
+MOO
+
+# print out y = antilog₂(x) table, aka pow2(x), 2^^x
+for (my $i = 0; $i < $GF_NO_ELEMENTS; $i++)
+{
+ print "\t" if ($i % 8 == 0);
+ print sprintf("0x%2.2x", $gfantilog[ $i ]);
+ print ',' unless ($i == $GF_MAX);
+ print ( (($i % 8) == 7) ? "\n" : ' ' );
+}
+
+print<<MOO;
+};
+
+#ifdef CONFIG_GALOIS_MUL_LUT
+const pgm_gf8_t pgm_gftable[PGM_GF_NO_ELEMENTS * PGM_GF_NO_ELEMENTS] =
+{
+MOO
+
+sub gfmul {
+ my($a, $b) = @_;
+ return 0 if ($a == 0 || $b == 0);
+ my $sum = $gflog[ $a ] + $gflog[ $b ];
+ return ($sum >= $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<<MOO;
+};
+#endif
+
+/* eof */
+MOO
+
+# eof
diff --git a/3rdparty/openpgm-svn-r1085/pgm/gcov-parse.pl b/3rdparty/openpgm-svn-r1085/pgm/gcov-parse.pl
new file mode 100755
index 0000000..b87a3f3
--- /dev/null
+++ b/3rdparty/openpgm-svn-r1085/pgm/gcov-parse.pl
@@ -0,0 +1,41 @@
+#!/usr/bin/perl
+
+$type = '';
+$target = '';
+
+while (<>) {
+ 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 <errno.h>
+#ifdef CONFIG_HAVE_GETIFADDRS
+# include <sys/types.h>
+# include <ifaddrs.h>
+#endif
+#if defined( sun )
+# include <sys/sockio.h>
+#endif
+#if defined( _WIN32 )
+# include <ws2tcpip.h>
+# include <iphlpapi.h>
+#endif
+#include <impl/i18n.h>
+#include <impl/framework.h>
+
+
+//#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 <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <errno.h>
+#ifndef _WIN32
+# include <netdb.h>
+#endif
+#include <impl/i18n.h>
+#include <impl/framework.h>
+
+
+//#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 <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <errno.h>
+#include <stdio.h>
+#ifndef _WIN32
+# include <netdb.h>
+#endif
+#include <impl/i18n.h>
+#include <impl/framework.h>
+
+
+//#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 <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <impl/framework.h>
+
+
+//#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 <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <impl/framework.h>
+
+
+//#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, "<PRE>");
+ write_ascii (histogram, "<BR/>", string);
+ pgm_string_append (string, "</PRE>");
+}
+
+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 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>OpenPGM - Page Not Found</title>
+ <link ref="stylesheet" href="/base.css" type="text/css" charset="utf-8" />
+</head>
+<body>
+ <h1>Lah, page not found.</h1>
+ <p><a href="/">Return to main page</a></p>
+</body>
+</html>
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 $/; <MOO> };
+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<<MOO;
+#define WWW_$var "$all"
+MOO
diff --git a/3rdparty/openpgm-svn-r1085/pgm/htdocs/robots.txt b/3rdparty/openpgm-svn-r1085/pgm/htdocs/robots.txt
new file mode 100644
index 0000000..1f53798
--- /dev/null
+++ b/3rdparty/openpgm-svn-r1085/pgm/htdocs/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/3rdparty/openpgm-svn-r1085/pgm/htdocs/xhtml10_strict.doctype b/3rdparty/openpgm-svn-r1085/pgm/htdocs/xhtml10_strict.doctype
new file mode 100644
index 0000000..6a3e4f9
--- /dev/null
+++ b/3rdparty/openpgm-svn-r1085/pgm/htdocs/xhtml10_strict.doctype
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
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 <errno.h>
+#include <inttypes.h>
+#ifndef _WIN32
+# include <netdb.h>
+#else
+# include <io.h>
+# include <lmcons.h>
+# include <process.h>
+#endif
+#include <stdio.h>
+#include <time.h>
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/receiver.h>
+#include <impl/socket.h>
+#include <pgm/version.h>
+
+#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<head>"
+ "<title>%s - %s</title>"
+ "<link rel=\"stylesheet\" href=\"/base.css\" type=\"text/css\" charset=\"utf-8\" />"
+ "</head>\n"
+ "<body>"
+ "<div id=\"header\">"
+ "<span id=\"hostname\">%s</span>"
+ " | <span id=\"banner\"><a href=\"http://code.google.com/p/openpgm/\">OpenPGM</a> %u.%u.%u</span>"
+ " | <span id=\"timestamp\">%s</span>"
+ "</div>"
+ "<div id=\"navigation\">"
+ "<a href=\"/\"><span class=\"tab\" id=\"tab%s\">General Information</span></a>"
+ "<a href=\"/interfaces\"><span class=\"tab\" id=\"tab%s\">Interfaces</span></a>"
+ "<a href=\"/transports\"><span class=\"tab\" id=\"tab%s\">Transports</span></a>"
+#ifdef CONFIG_HISTOGRAMS
+ "<a href=\"/histograms\"><span class=\"tab\" id=\"tab%s\">Histograms</span></a>"
+#endif
+ "<div id=\"tabline\"></div>"
+ "</div>"
+ "<div id=\"content\">",
+ 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, "</div>"
+ "<div id=\"footer\">"
+ "&copy;2010 Miru"
+ "</div>"
+ "</body>\n"
+ "</html>");
+
+ 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, "<table>"
+ "<tr>"
+ "<th>host name:</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>user name:</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>IP address:</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>transports:</th><td><a href=\"/transports\">%i</a></td>"
+ "</tr><tr>"
+ "<th>process ID:</th><td>%i</td>"
+ "</tr>"
+ "</table>\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, "<PRE>");
+ 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<BR/>\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<BR/>\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, "</PRE>\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, "<div class=\"bubbly\">"
+ "\n<table cellspacing=\"0\">"
+ "<tr>"
+ "<th>Group address</th>"
+ "<th>Dest port</th>"
+ "<th>Source GSI</th>"
+ "<th>Source port</th>"
+ "</tr>"
+ );
+
+ 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, "<tr>"
+ "<td>%s</td>"
+ "<td>%i</td>"
+ "<td><a href=\"/%s.%u\">%s</a></td>"
+ "<td><a href=\"/%s.%u\">%u</a></td>"
+ "</tr>",
+ 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, "<tr>"
+ "<td colspan=\"6\"><div class=\"empty\">This transport has no peers.</div></td>"
+ "</tr>"
+ );
+ }
+
+ pgm_string_append (response, "</table>\n"
+ "</div>");
+ 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, "<div class=\"heading\">"
+ "<strong>Transport: </strong>"
+ "%s.%hu"
+ "</div>",
+ gsi, sport);
+
+/* peers */
+
+ pgm_string_append (response, "<div class=\"bubbly\">"
+ "\n<table cellspacing=\"0\">"
+ "<tr>"
+ "<th>Group address</th>"
+ "<th>Dest port</th>"
+ "<th>Source address</th>"
+ "<th>Last hop</th>"
+ "<th>Source GSI</th>"
+ "<th>Source port</th>"
+ "</tr>"
+ );
+
+ 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, "<tr>"
+ "<td colspan=\"6\"><div class=\"empty\">This transport has no peers.</div></td>"
+ "</tr>"
+ );
+
+ }
+
+ pgm_string_append (response, "</table>\n"
+ "</div>");
+
+/* source and configuration information */
+
+ pgm_string_append_printf (response, "<div class=\"rounded\" id=\"information\">"
+ "\n<table>"
+ "<tr>"
+ "<th>Source address</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>Group address</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>Dest port</th><td>%u</td>"
+ "</tr><tr>"
+ "<th>Source GSI</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>Source port</th><td>%u</td>"
+ "</tr>",
+ source_address,
+ group_address,
+ dport,
+ gsi,
+ sport);
+
+/* continue with source information */
+
+ pgm_string_append_printf (response, "<tr>"
+ "<td colspan=\"2\"><div class=\"break\"></div></td>"
+ "</tr><tr>"
+ "<th>Ttl</th><td>%u</td>"
+ "</tr><tr>"
+ "<th>Adv Mode</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>Late join</th><td>disable(2)</td>"
+ "</tr><tr>"
+ "<th>TXW_MAX_RTE</th><td>%" GROUP_FORMAT "zd</td>"
+ "</tr><tr>"
+ "<th>TXW_SECS</th><td>%" GROUP_FORMAT "u</td>"
+ "</tr><tr>"
+ "<th>TXW_ADV_SECS</th><td>0</td>"
+ "</tr><tr>"
+ "<th>Ambient SPM interval</th><td>%" GROUP_FORMAT PGM_TIME_FORMAT " ms</td>"
+ "</tr><tr>"
+ "<th>IHB_MIN</th><td>%" GROUP_FORMAT PGM_TIME_FORMAT " ms</td>"
+ "</tr><tr>"
+ "<th>IHB_MAX</th><td>%" GROUP_FORMAT PGM_TIME_FORMAT " ms</td>"
+ "</tr><tr>"
+ "<th>NAK_BO_IVL</th><td>%" GROUP_FORMAT PGM_TIME_FORMAT " ms</td>"
+ "</tr><tr>"
+ "<th>FEC</th><td>disabled(1)</td>"
+ "</tr><tr>"
+ "<th>Source Path Address</th><td>%s</td>"
+ "</tr>"
+ "</table>\n"
+ "</div>",
+ 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<h2>Performance information</h2>"
+ "\n<table>"
+ "<tr>"
+ "<th>Data bytes sent</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Data packets sent</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Bytes buffered</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Packets buffered</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Bytes sent</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Raw NAKs received</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Checksum errors</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Malformed NAKs</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Packets discarded</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Bytes retransmitted</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Packets retransmitted</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAKs received</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAKs ignored</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Transmission rate</th><td>%" GROUP_FORMAT PRIu32 " bps</td>"
+ "</tr><tr>"
+ "<th>NNAK packets received</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NNAKs received</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Malformed NNAKs</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr>"
+ "</table>\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, "<tr>"
+ "<td>%s</td>"
+ "<td>%u</td>"
+ "<td>%s</td>"
+ "<td>%s</td>"
+ "<td><a href=\"/%s.%u\">%s</a></td>"
+ "<td><a href=\"/%s.%u\">%u</a></td>"
+ "</tr>",
+ 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, "<div class=\"heading\">"
+ "<strong>Peer: </strong>"
+ "%s.%u"
+ "</div>",
+ gsi, sport);
+
+
+/* peer information */
+ pgm_string_append_printf (response, "<div class=\"rounded\" id=\"information\">"
+ "\n<table>"
+ "<tr>"
+ "<th>Group address</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>Dest port</th><td>%u</td>"
+ "</tr><tr>"
+ "<th>Source address</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>Last hop</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>Source GSI</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>Source port</th><td>%u</td>"
+ "</tr>",
+ group_address,
+ dport,
+ source_address,
+ last_hop,
+ gsi,
+ sport);
+
+ pgm_string_append_printf (response, "<tr>"
+ "<td colspan=\"2\"><div class=\"break\"></div></td>"
+ "</tr><tr>"
+ "<th>NAK_BO_IVL</th><td>%" GROUP_FORMAT PGM_TIME_FORMAT " ms</td>"
+ "</tr><tr>"
+ "<th>NAK_RPT_IVL</th><td>%" GROUP_FORMAT PGM_TIME_FORMAT " ms</td>"
+ "</tr><tr>"
+ "<th>NAK_NCF_RETRIES</th><td>%" GROUP_FORMAT "u</td>"
+ "</tr><tr>"
+ "<th>NAK_RDATA_IVL</th><td>%" GROUP_FORMAT PGM_TIME_FORMAT " ms</td>"
+ "</tr><tr>"
+ "<th>NAK_DATA_RETRIES</th><td>%" GROUP_FORMAT "u</td>"
+ "</tr><tr>"
+ "<th>Send NAKs</th><td>enabled(1)</td>"
+ "</tr><tr>"
+ "<th>Late join</th><td>disabled(2)</td>"
+ "</tr><tr>"
+ "<th>NAK TTL</th><td>%u</td>"
+ "</tr><tr>"
+ "<th>Delivery order</th><td>ordered(2)</td>"
+ "</tr><tr>"
+ "<th>Multicast NAKs</th><td>disabled(2)</td>"
+ "</tr>"
+ "</table>\n"
+ "</div>",
+ 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<h2>Performance information</h2>"
+ "\n<table>"
+ "<tr>"
+ "<th>Data bytes received</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Data packets received</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAK failures</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Bytes received</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Checksum errors</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Malformed SPMs</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Malformed ODATA</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Malformed RDATA</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Malformed NCFs</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Packets discarded</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Losses</th><td>%" GROUP_FORMAT PRIu32 "</td>" /* detected missed packets */
+ "</tr><tr>"
+ "<th>Bytes delivered to app</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Packets delivered to app</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Duplicate SPMs</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Duplicate ODATA/RDATA</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAK packets sent</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAKs sent</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAKs retransmitted</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAKs failed</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAKs failed due to RXW advance</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAKs failed due to NCF retries</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAKs failed due to DATA retries</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAK failures delivered to app</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAKs suppressed</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Malformed NAKs</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Outstanding NAKs</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>Last activity</th><td>%s</td>"
+ "</tr><tr>"
+ "<th>NAK repair min time</th><td>%" GROUP_FORMAT PRIu32 " μs</td>"
+ "</tr><tr>"
+ "<th>NAK repair mean time</th><td>%" GROUP_FORMAT PRIu32 " μs</td>"
+ "</tr><tr>"
+ "<th>NAK repair max time</th><td>%" GROUP_FORMAT PRIu32 " μs</td>"
+ "</tr><tr>"
+ "<th>NAK fail min time</th><td>%" GROUP_FORMAT PRIu32 " μs</td>"
+ "</tr><tr>"
+ "<th>NAK fail mean time</th><td>%" GROUP_FORMAT PRIu32 " μs</td>"
+ "</tr><tr>"
+ "<th>NAK fail max time</th><td>%" GROUP_FORMAT PRIu32 " μs</td>"
+ "</tr><tr>"
+ "<th>NAK min retransmit count</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAK mean retransmit count</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr><tr>"
+ "<th>NAK max retransmit count</th><td>%" GROUP_FORMAT PRIu32 "</td>"
+ "</tr>"
+ "</table>\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 <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <check.h>
+
+#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 <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#ifndef _WIN32
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netdb.h> /* _GNU_SOURCE for EAI_NODATA */
+#endif
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <pgm/if.h>
+
+
+//#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::]
+ * <hostname>
+ * <nss network name>
+ *
+ * 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, /* <struct interface_req*> */
+ 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, /* <struct interface_req*> */
+ pgm_list_t** restrict recv_list, /* <struct group_source_req*> */
+ 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, /* <struct interface_req*> */
+ pgm_list_t** restrict recv_list, /* <struct group_source_req*> */
+ pgm_list_t** restrict send_list, /* <struct group_source_req*> */
+ 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, /* <struct group_source_req*> */
+ pgm_list_t** restrict send_list, /* <struct group_source_req*> */
+ 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; /* <struct group_source_req*> */
+ pgm_list_t* send_list = NULL; /* <struct group_source_req*> */
+
+ 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 <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 &ne;
+ }
+ 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 "<hostname>" 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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_CHECKSUM_H__
+#define __PGM_IMPL_CHECKSUM_H__
+
+#include <pgm/types.h>
+
+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 <ws2tcpip.h>
+# include <mswsock.h>
+#endif
+#include <impl/framework.h>
+
+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 <framework.h> 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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_FIXED_H__
+#define __PGM_IMPL_FIXED_H__
+
+#include <pgm/types.h>
+
+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 <pgm/atomic.h>
+#include <pgm/error.h>
+#include <pgm/gsi.h>
+#include <pgm/list.h>
+#include <pgm/macros.h>
+#include <pgm/mem.h>
+#include <pgm/messages.h>
+#include <pgm/msgv.h>
+#include <pgm/packet.h>
+#include <pgm/skbuff.h>
+#include <pgm/socket.h>
+#include <pgm/time.h>
+#include <pgm/tsi.h>
+#include <pgm/types.h>
+
+#include <impl/checksum.h>
+#include <impl/features.h>
+#include <impl/fixed.h>
+#include <impl/galois.h>
+#include <impl/getifaddrs.h>
+#include <impl/getnodeaddr.h>
+#include <impl/hashtable.h>
+#include <impl/histogram.h>
+#include <impl/indextoaddr.h>
+#include <impl/indextoname.h>
+#include <impl/inet_network.h>
+#include <impl/ip.h>
+#include <impl/list.h>
+#include <impl/math.h>
+#include <impl/md5.h>
+#include <impl/messages.h>
+#include <impl/nametoindex.h>
+#include <impl/notify.h>
+#include <impl/processor.h>
+#include <impl/queue.h>
+#include <impl/rand.h>
+#include <impl/rate_control.h>
+#include <impl/reed_solomon.h>
+#include <impl/slist.h>
+#include <impl/sn.h>
+#include <impl/sockaddr.h>
+#include <impl/string.h>
+#include <impl/thread.h>
+#include <impl/time.h>
+#include <impl/tsi.h>
+#include <impl/wsastrerror.h>
+
+#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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_GALOIS_H__
+#define __PGM_IMPL_GALOIS_H__
+
+#include <pgm/types.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_GETIFADDRS_H__
+#define __PGM_IMPL_GETIFADDRS_H__
+
+#ifndef _WIN32
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <net/if.h>
+#else
+# include <ws2tcpip.h>
+#endif
+
+struct pgm_ifaddrs_t;
+
+#include <pgm/types.h>
+#include <pgm/error.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_GETNODEADDR_H__
+#define __PGM_IMPL_GETNODEADDR_H__
+
+#ifndef _WIN32
+# include <sys/socket.h>
+#endif
+#include <pgm/types.h>
+#include <pgm/error.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_HASHTABLE_H__
+#define __PGM_IMPL_HASHTABLE_H__
+
+#include <pgm/types.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_HISTOGRAM_H__
+#define __PGM_IMPL_HISTOGRAM_H__
+
+#include <pgm/types.h>
+#include <pgm/time.h>
+#include <impl/slist.h>
+#include <impl/string.h>
+
+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 <libintl.h>
+# 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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_INDEXTOADDR_H__
+#define __PGM_IMPL_INDEXTOADDR_H__
+
+#ifndef _WIN32
+# include <sys/socket.h>
+#endif
+#include <pgm/types.h>
+#include <pgm/error.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_INDEXTONAME_H__
+#define __PGM_IMPL_INDEXTONAME_H__
+
+#include <pgm/types.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_INET_NETWORK_H__
+#define __PGM_IMPL_INET_NETWORK_H__
+
+#ifndef _WIN32
+# include <netinet/in.h>
+#endif
+#include <pgm/types.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_IP_H__
+#define __PGM_IMPL_IP_H__
+
+#ifndef _WIN32
+# include <netinet/in.h>
+#endif
+#include <sys/param.h>
+#include <pgm/types.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_LIST_H__
+#define __PGM_IMPL_LIST_H__
+
+#include <pgm/types.h>
+#include <pgm/list.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_MATH_H__
+#define __PGM_IMPL_MATH_H__
+
+#include <pgm/types.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_MD5_H__
+#define __PGM_IMPL_MD5_H__
+
+struct pgm_md5_t;
+
+#include <pgm/types.h>
+
+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/types.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_MESSAGES_H__
+#define __PGM_IMPL_MESSAGES_H__
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <pgm/types.h>
+#include <pgm/messages.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_NAMETOINDEX_H__
+#define __PGM_IMPL_NAMETOINDEX_H__
+
+#ifndef _WIN32
+# include <sys/socket.h>
+#endif
+#include <pgm/types.h>
+
+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 <sys/socket.h>
+#endif
+#include <impl/framework.h>
+
+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 <framework.h> 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 <fcntl.h>
+# include <unistd.h>
+# ifdef CONFIG_HAVE_EVENTFD
+# include <sys/eventfd.h>
+# endif
+#else /* _WIN32 */
+# include <memory.h>
+# include <ws2tcpip.h>
+#endif
+#include <pgm/types.h>
+#include <impl/messages.h>
+
+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 <sys/socket.h>
+#endif
+#include <impl/framework.h>
+
+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 <netinet/in.h>
+#endif
+#include <impl/framework.h>
+
+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 <impl/framework.h>
+
+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 <framework.h> 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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_QUEUE_H__
+#define __PGM_IMPL_QUEUE_H__
+
+typedef struct pgm_queue_t pgm_queue_t;
+
+#include <pgm/types.h>
+#include <pgm/list.h>
+
+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 <framework.h> 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/types.h>
+
+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 <framework.h> 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 <pgm/types.h>
+#include <pgm/time.h>
+#include <impl/thread.h>
+
+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 <sys/socket.h>
+#endif
+#include <impl/framework.h>
+#include <impl/rxw.h>
+
+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 <framework.h> 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 <pgm/types.h>
+#include <impl/galois.h>
+
+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 <impl/framework.h>
+
+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 <framework.h> 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/types.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_SN_H__
+#define __PGM_IMPL_SN_H__
+
+#include <pgm/types.h>
+#include <impl/messages.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_SOCKADDR_H__
+#define __PGM_IMPL_SOCKADDR_H__
+
+#ifndef _WIN32
+# include <sys/socket.h>
+#endif
+#include <pgm/types.h>
+
+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 <impl/framework.h>
+#include <impl/txw.h>
+#include <impl/source.h>
+
+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 <impl/framework.h>
+#include <impl/receiver.h>
+
+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 <impl/framework.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_STRING_H__
+#define __PGM_IMPL_STRING_H__
+
+typedef struct pgm_string_t pgm_string_t;
+
+#include <stdarg.h>
+#include <pgm/types.h>
+
+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 <framework.h> 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 <pthread.h>
+# include <unistd.h>
+#else
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+#include <pgm/types.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_TIME_H__
+#define __PGM_IMPL_TIME_H__
+
+#include <pgm/types.h>
+#include <pgm/error.h>
+#include <pgm/time.h>
+
+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 <impl/framework.h>
+#include <impl/socket.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_TSI_H__
+#define __PGM_IMPL_TSI_H__
+
+#include <pgm/types.h>
+#include <pgm/tsi.h>
+#include <impl/hashtable.h>
+
+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 <impl/framework.h>
+
+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 <framework.h> can be included directly."
+#endif
+
+#ifndef __PGM_IMPL_WSASTRERROR_H__
+#define __PGM_IMPL_WSASTRERROR_H__
+
+#include <pgm/types.h>
+
+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 <atomic.h>
+#endif
+#include <pgm/types.h>
+
+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/types.h>
+
+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 <pgm/types.h>
+#include <pgm/error.h>
+
+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/types.h>
+
+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 <pgm/types.h>
+#include <pgm/error.h>
+
+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 <pgm/types.h>
+#include <pgm/error.h>
+
+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/types.h>
+
+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 <pgm/pgm.h>
+}
+
+namespace ip {
+
+class pgm
+{
+public:
+ /// The type of a PGM endpoint.
+ typedef pgm_endpoint<pgm> 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<pgm> 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 <pgm/pgm.h>
+}
+
+namespace ip {
+
+template <typename InternetProtocol>
+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<InternetProtocol>& e1,
+ const pgm_endpoint<InternetProtocol>& e2)
+ {
+ return e1.address() == e2.address() && e1.port() == e2.port();
+ }
+
+ /// Compare two endpoints for inequality.
+ friend bool operator!= (const pgm_endpoint<InternetProtocol>& e1,
+ const pgm_endpoint<InternetProtocol>& e2)
+ {
+ return e1.address() != e2.address() || e1.port() != e2.port();
+ }
+
+ /// Compare endpoints for ordering.
+ friend bool operator<(const pgm_endpoint<InternetProtocol>& e1,
+ const pgm_endpoint<InternetProtocol>& 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/types.h>
+
+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/types.h>
+
+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 <stddef.h>
+
+
+/* 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 <alloca.h>
+#elif defined(_WIN32)
+# include <malloc.h>
+#else
+# include <stdlib.h>
+#endif
+#include <pgm/types.h>
+
+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/types.h>
+
+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 <pgm/types.h>
+#include <pgm/packet.h>
+#include <pgm/skbuff.h>
+
+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 <sys/socket.h>
+# include <netinet/in.h>
+# include <netinet/ip.h>
+#endif
+#include <pgm/types.h>
+
+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 <pgm/atomic.h>
+#include <pgm/engine.h>
+#include <pgm/error.h>
+#include <pgm/gsi.h>
+#include <pgm/if.h>
+#include <pgm/macros.h>
+#include <pgm/mem.h>
+#include <pgm/messages.h>
+#include <pgm/msgv.h>
+#include <pgm/packet.h>
+#include <pgm/skbuff.h>
+#include <pgm/socket.h>
+#include <pgm/time.h>
+#include <pgm/tsi.h>
+#include <pgm/types.h>
+#include <pgm/version.h>
+
+#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 <pgm/pgm_socket.hh>
+#include <pgm/ip/pgm_endpoint.hh>
+#include <pgm/ip/pgm.hh>
+
+#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 <cerrno>
+#ifndef _WIN32
+# include <cstddef>
+# include <sys/socket.h>
+#else
+# include <ws2tcpip.h>
+#endif
+
+namespace cpgm {
+#define restrict
+#include <pgm/pgm.h>
+};
+
+template <typename Protocol>
+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 <signal.h>
+#include <glib.h>
+
+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 <string.h>
+
+struct pgm_sk_buff_t;
+
+#include <pgm/types.h>
+#include <pgm/atomic.h>
+#include <pgm/mem.h>
+#include <pgm/list.h>
+#include <pgm/time.h>
+#include <pgm/packet.h>
+#include <pgm/tsi.h>
+#include <pgm/socket.h>
+
+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 <pgm/types.h>
+#include <pgm/error.h>
+
+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 <poll.h>
+#endif
+#ifdef CONFIG_HAVE_EPOLL
+# include <sys/epoll.h>
+#endif
+#ifndef _WIN32
+# include <sys/select.h>
+# include <sys/socket.h>
+#endif
+#include <pgm/types.h>
+#include <pgm/error.h>
+#include <pgm/msgv.h>
+#include <pgm/tsi.h>
+
+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/types.h>
+
+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 <pgm/types.h>
+#include <pgm/gsi.h>
+
+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 <sys/param.h>
+#endif
+#include <pgm/macros.h>
+
+#ifdef _WIN32
+# include <ws2tcpip.h>
+# define sa_family_t ULONG
+#endif
+
+#ifdef _MSC_VER
+# include <pgm/winint.h>
+# define bool BOOL
+# define ssize_t SSIZE_T
+# define restrict
+#elif !defined( __cplusplus) || (__GNUC__ >= 4)
+/* g++ v4 handles C99 headers without complaints */
+# include <stdbool.h>
+# include <stdint.h>
+#else
+/* g++ v3 and other ancient compilers */
+# define bool int
+# include <stdint.h>
+#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/types.h>
+
+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 <limits.h>
+
+/* 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 <gwyn@arl.mil>:
+ "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 <impl/i18n.h>
+#include <impl/framework.h>
+
+
+//#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 <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <ws2tcpip.h>
+# include <iphlpapi.h>
+#endif
+#include <impl/framework.h>
+
+
+//#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 <ctype.h>
+#include <impl/framework.h>
+
+
+//#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 <errno.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <impl/framework.h>
+
+
+//#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 <unistd.h>
+#include <stdio.h>
+#include <glib.h>
+#ifndef G_OS_WIN32
+# include <netdb.h>
+#endif
+#include <pgm/pgm.h>
+#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 <impl/framework.h>
+
+
+//#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 <impl/framework.h>
+
+
+//#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 <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <ctype.h>
+#include <stdio.h>
+#ifdef _WIN32
+# define strcasecmp stricmp
+#endif
+#include <impl/framework.h>
+#include <impl/mem.h>
+
+
+//#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 <stdarg.h>
+#include <stdio.h>
+#include <impl/framework.h>
+
+
+/* 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 <ws2tcpip.h>
+# include <iphlpapi.h>
+#endif
+#include <impl/i18n.h>
+#include <impl/framework.h>
+
+
+//#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 <errno.h>
+#ifdef CONFIG_HAVE_POLL
+# include <poll.h>
+#endif
+#ifndef _WIN32
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#endif
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/net.h>
+#include <impl/socket.h>
+
+
+#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 <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <inttypes.h>
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/packet_parse.h>
+
+
+//#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 <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <inttypes.h>
+#include <ctype.h>
+#include <stdio.h>
+#ifndef _WIN32
+# include <sys/socket.h>
+# include <netdb.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#endif
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/packet_test.h>
+
+
+//#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 <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/receiver.h>
+#include <impl/socket.h>
+
+#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 <signal.h>
+#include <stdlib.h>
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <glib.h>
+#include <check.h>
+
+#include <pgm/time.h>
+
+
+/* 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 <impl/framework.h>
+
+
+//#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 <errno.h>
+# include <stdio.h>
+#endif
+#include <impl/framework.h>
+
+
+//#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 <impl/framework.h>
+
+
+/* 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 <inttypes.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <inttypes.h>
+#include <errno.h>
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/receiver.h>
+#include <impl/sqn_list.h>
+#include <impl/timer.h>
+#include <impl/packet_parse.h>
+#include <impl/net.h>
+
+
+//#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 <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <errno.h>
+#ifndef _WIN32
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h> /* _GNU_SOURCE for in6_pktinfo */
+#else
+# include <ws2tcpip.h>
+# include <mswsock.h>
+#endif
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/source.h>
+#include <impl/packet_parse.h>
+#include <impl/timer.h>
+#include <impl/engine.h>
+
+
+//#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 <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h> /* _GNU_SOURCE for in6_pktinfo */
+#include <arpa/inet.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <impl/framework.h>
+
+
+/* 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 <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <inttypes.h>
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/rxw.h>
+
+
+//#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 <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <check.h>
+#include <glib.h>
+
+
+/* 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 <fcntl.h>
+#include <signal.h> /* _GNU_SOURCE for strsignal() */
+#include <glib.h>
+#ifndef G_OS_WIN32
+# include <unistd.h>
+#else
+# include <io.h>
+#endif
+#include <pgm/pgm.h>
+#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 <signal.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <impl/framework.h>
+#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 <impl/framework.h>
+
+
+//#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 <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+#include <errno.h>
+#include <impl/i18n.h>
+#include <impl/framework.h>
+
+#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 <signal.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+#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 <errno.h>
+#ifndef _WIN32
+# include <sys/socket.h>
+# include <netdb.h>
+#endif
+#include <impl/framework.h>
+
+
+/* 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 <sys/ioctl.h>
+#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.<imr_ifindex>
+ *
+ * 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 <errno.h>
+#ifdef CONFIG_HAVE_POLL
+# include <poll.h>
+#endif
+#ifdef CONFIG_HAVE_EPOLL
+# include <sys/epoll.h>
+#endif
+#include <stdio.h>
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/socket.h>
+#include <impl/receiver.h>
+#include <impl/source.h>
+#include <impl/timer.h>
+
+
+//#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 <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <inttypes.h>
+#include <errno.h>
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/socket.h>
+#include <impl/source.h>
+#include <impl/sqn_list.h>
+#include <impl/packet_parse.h>
+#include <impl/net.h>
+
+
+//#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 <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <limits.h>
+#include <stdarg.h>
+#include <stdio.h> /* _GNU_SOURCE for vasprintf */
+#include <string.h>
+#include <impl/framework.h>
+
+
+//#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 <errno.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <regex.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+
+#include <pgm/pgm.h>
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+#include <pgm/gsi.h>
+#include <pgm/signal.h>
+
+#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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : 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 <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <pgm/pgm.h>
+#include "async.h"
+
+
+//#define ASYNC_DEBUG
+
+#ifndef ASYNC_DEBUG
+# define g_trace(...) while (0)
+#else
+#include <ctype.h>
+# 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 <errno.h>
+#include <glib.h>
+#include <pgm/pgm.h>
+
+
+#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 <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+
+#include <pgm/pgm.h>
+#include <pgm/packet_test.h>
+
+#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 <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+
+#include <pgm/pgm.h>
+#include <pgm/backtrace.h>
+#include <pgm/signal.h>
+#include <pgm/log.h>
+
+#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 <errno.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <regex.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+
+#include <pgm/pgm.h>
+#include <pgm/backtrace.h>
+#include <pgm/log.h>
+#include <pgm/signal.h>
+#include <pgm/sqn_list.h>
+#include <pgm/packet_parse.h>
+
+#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 <network> : Multicast group or unicast IP address\n");
+ fprintf (stderr, " -s <port> : 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 <errno.h>
+#include <impl/framework.h>
+
+//#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 <inttypes.h>
+#include <errno.h>
+#include <stdlib.h>
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include "windows.h"
+#endif
+#include <impl/i18n.h>
+#include <impl/framework.h>
+
+//#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 <time.h>
+static pgm_time_t pgm_clock_update (void);
+#endif
+#ifdef CONFIG_HAVE_FTIME
+# include <sys/timeb.h>
+# ifdef _WIN32
+# define ftime _ftime
+# endif
+static pgm_time_t pgm_ftime_update (void);
+#endif
+#ifdef CONFIG_HAVE_GETTIMEOFDAY
+# include <sys/time.h>
+static pgm_time_t pgm_gettimeofday_update (void);
+#endif
+#ifdef CONFIG_HAVE_HPET
+# include <fcntl.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+# include <sys/mman.h>
+# 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 <fcntl.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+# include <sys/ioctl.h>
+# include <linux/rtc.h>
+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 <stdio.h>
+# include <string.h>
+# 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 <math.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <inttypes.h>
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/timer.h>
+#include <impl/receiver.h>
+#include <impl/source.h>
+
+
+//#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 <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <stdio.h>
+#include <impl/framework.h>
+
+
+//#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 <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <check.h>
+
+
+/* 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 <inttypes.h>
+#include <impl/i18n.h>
+#include <impl/framework.h>
+#include <impl/txw.h>
+
+
+//#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 <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <check.h>
+#include <glib.h>
+
+
+/* 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 <impl/framework.h>
+#include <pgm/version.h>
+
+
+/* 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 <ws2ipdef.h>
+
++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 <impl/i18n.h>
+#include <impl/framework.h>
+
+#ifdef _WIN32
+# include <ws2tcpip.h>
+
+
+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 */