summaryrefslogtreecommitdiffstats
path: root/3rdparty/openpgm-svn-r1135/pgm/packet_test.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/openpgm-svn-r1135/pgm/packet_test.c')
-rw-r--r--3rdparty/openpgm-svn-r1135/pgm/packet_test.c1162
1 files changed, 1162 insertions, 0 deletions
diff --git a/3rdparty/openpgm-svn-r1135/pgm/packet_test.c b/3rdparty/openpgm-svn-r1135/pgm/packet_test.c
new file mode 100644
index 0000000..267700a
--- /dev/null
+++ b/3rdparty/openpgm-svn-r1135/pgm/packet_test.c
@@ -0,0 +1,1162 @@
+/* vim:ts=8:sts=8:sw=4:noai:noexpandtab
+ *
+ * PGM packet formats, RFC 3208.
+ *
+ * Copyright (c) 2006-2010 Miru Limited.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define __STDC_FORMAT_MACROS
+#ifdef _MSC_VER
+# include <pgm/wininttypes.h>
+#else
+# include <inttypes.h>
+#endif
+#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 */