summaryrefslogtreecommitdiffstats
path: root/3rdparty/openpgm-svn-r1135/pgm/test/dump-json.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/openpgm-svn-r1135/pgm/test/dump-json.c')
-rw-r--r--3rdparty/openpgm-svn-r1135/pgm/test/dump-json.c1292
1 files changed, 1292 insertions, 0 deletions
diff --git a/3rdparty/openpgm-svn-r1135/pgm/test/dump-json.c b/3rdparty/openpgm-svn-r1135/pgm/test/dump-json.c
new file mode 100644
index 0000000..adbc217
--- /dev/null
+++ b/3rdparty/openpgm-svn-r1135/pgm/test/dump-json.c
@@ -0,0 +1,1292 @@
+/* vim:ts=8:sts=8:sw=4:noai:noexpandtab
+ *
+ * JSON packet dump.
+ *
+ * Copyright (c) 2006-2008 Miru Limited.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <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 <impl/framework.h>
+#include <impl/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 pgm_ip)) {
+ printf ("\t\"message\": \"IP: bad IP header length %i, should be at least %" G_GSIZE_FORMAT "lu bytes.\",\n", ip_header_length, sizeof(struct pgm_ip));
+ return -1;
+ }
+
+/* ip_len can equal packet_length - ip_header_length in FreeBSD/NetBSD
+ * Stevens/Fenner/Rudolph, Unix Network Programming Vol.1, p.739
+ *
+ * RFC3828 allows partial packets such that len < packet_length with UDP lite
+ */
+ guint packet_length = g_ntohs(ip->ip_len); /* total packet length */
+ if (len < packet_length) { /* redundant: often handled in kernel */
+ printf ("\t\"message\": \"IP: truncated IP packet: header reports %i actual length %i bytes.\",\n", (int)len, (int)packet_length);
+ return -1;
+ }
+
+/* TCP Segmentation Offload (TSO) might have zero length here */
+ if (packet_length < ip_header_length) {
+ printf ("\t\"message\": \"IP: header reports %i less than IP header length %i.\",\n", (int)packet_length, (int)ip_header_length);
+ return -1;
+ }
+
+/* packets that fail checksum will generally not be passed upstream except with rfc3828
+ */
+ int sum = pgm_inet_checksum((char*)ip, ip_header_length, 0);
+ if (sum != 0) {
+ int ip_sum = g_ntohs(ip->ip_sum);
+ printf ("\t\"message\": \"IP: IP header checksum incorrect: 0x%x.\",\n", ip_sum);
+ return -2;
+ }
+
+ if (ip->ip_p != IPPROTO_PGM) {
+ printf ("\t\"message\": \"IP: packet IP protocol not PGM: %i.\",\n", ip->ip_p);
+ return -1;
+ }
+
+/* fragmentation offset, bit 0: 0, bit 1: do-not-fragment, bit 2: more-fragments */
+ int offset = g_ntohs(ip->ip_off);
+ if ((offset & 0x1fff) != 0) {
+ printf ("\t\"message\": \"IP: fragmented IP packet, ignoring.\",\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+print_ip_header (
+ struct pgm_ip* ip
+ )
+{
+ puts ("\t\"IP\": {");
+ printf ("\t\t\"version\": %i,\n",
+ ip->ip_v
+ );
+ printf ("\t\t\"headerLength\": %i,\n",
+ ip->ip_hl
+ );
+ printf ("\t\t\"ToS\": %i,\n",
+ ip->ip_tos & 0x3
+ );
+ printf ("\t\t\"length\": %i,\n",
+ g_ntohs(ip->ip_len)
+ );
+ printf ("\t\t\"fragmentId\": %i,\n",
+ g_ntohs(ip->ip_id)
+ );
+ printf ("\t\t\"DF\": %s,\n",
+ (g_ntohs(ip->ip_off) & 0x4000) ? "true" : "false"
+ );
+ printf ("\t\t\"MF\": %s,\n",
+ (g_ntohs(ip->ip_off) & 0x2000) ? "true" : "false"
+ );
+ printf ("\t\t\"fragmentOffset\": %i,\n",
+ g_ntohs(ip->ip_off) & 0x1fff
+ );
+ printf ("\t\t\"TTL\": %i,\n",
+ ip->ip_ttl
+ );
+ printf ("\t\t\"protocol\": %i,\n",
+ ip->ip_p
+ );
+ printf ("\t\t\"sourceIp\": \"%s\",\n",
+ inet_ntoa(*(struct in_addr*)&ip->ip_src)
+ );
+ printf ("\t\t\"destinationIp\": \"%s\",\n",
+ inet_ntoa(*(struct in_addr*)&ip->ip_dst)
+ );
+ puts ("\t\t\"IpOptions\": {");
+ puts ("\t\t}");
+ puts ("\t},");
+}
+
+int
+verify_pgm_header (
+ struct pgm_header* pgm,
+ guint pgm_len
+ )
+{
+
+/* PGM payload, header looks as follows:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Source Port | Destination Port |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Options | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Global Source ID ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | ... Global Source ID | TSDU Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type specific data ...
+ * +-+-+-+-+-+-+-+-+-+- ...
+ */
+ if (pgm_len < sizeof(pgm)) {
+ printf ("\t\"message\": \"PGM: packet size less than PGM header: %i bytes.\",\n", pgm_len);
+ return -1;
+ }
+
+ if (pgm->pgm_checksum)
+ {
+ int sum = pgm->pgm_checksum;
+ pgm->pgm_checksum = 0;
+ int pgm_sum = pgm_csum_fold (pgm_csum_partial ((const char*)pgm, pgm_len, 0));
+ pgm->pgm_checksum = sum;
+ if (pgm_sum != sum) {
+ printf ("\t\"message\": \"PGM: PGM packet checksum incorrect, packet 0x%x calculated 0x%x.\",\n", sum, pgm_sum);
+ return -2;
+ }
+ } else {
+ if (pgm->pgm_type != PGM_ODATA && pgm->pgm_type != PGM_RDATA) {
+ printf ("\t\"message\": \"PGM: No PGM checksum value, mandatory for ODATA/RDATA.\",\n");
+ return -1;
+ }
+ }
+
+ if ( pgm->pgm_type != PGM_SPM &&
+ pgm->pgm_type != PGM_POLL &&
+ pgm->pgm_type != PGM_POLR &&
+ pgm->pgm_type != PGM_ODATA &&
+ pgm->pgm_type != PGM_RDATA &&
+ pgm->pgm_type != PGM_NAK &&
+ pgm->pgm_type != PGM_NNAK &&
+ pgm->pgm_type != PGM_NCF &&
+ pgm->pgm_type != PGM_SPMR )
+ {
+ printf ("\t\"message\": \"PGM: Not a valid PGM packet type: %i.\",\n", pgm->pgm_type);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* note: output trails tsdu length line to allow for comma
+ */
+
+void
+print_pgm_header (
+ struct pgm_header* pgm
+ )
+{
+ puts ("\t\"PGM\": {");
+ printf ("\t\t\"sourcePort\": %i,\n", g_ntohs(pgm->pgm_sport));
+ printf ("\t\t\"destinationPort\": %i,\n", g_ntohs(pgm->pgm_dport));
+ printf ("\t\t\"type\": \"%s\",\n", pgm_type_string(pgm->pgm_type & 0xf));
+ printf ("\t\t\"version\": %i,\n", (pgm->pgm_type & 0xc0) >> 6);
+ puts ("\t\t\"options\": {");
+ printf ("\t\t\t\"networkSignificant\": %s,\n", (pgm->pgm_options & PGM_OPT_NETWORK) ? "true" : "false");
+ printf ("\t\t\t\"parityPacket\": %s,\n", (pgm->pgm_options & PGM_OPT_PARITY) ? "true" : "false");
+ printf ("\t\t\t\"variableLength\": %s\n", (pgm->pgm_options & PGM_OPT_VAR_PKTLEN) ? "true" : "false");
+ puts ("\t\t},");
+ printf ("\t\t\"checksum\": %i,\n", pgm->pgm_checksum);
+ printf ("\t\t\"gsi\": \"%i.%i.%i.%i.%i.%i\",\n",
+ pgm->pgm_gsi[0],
+ pgm->pgm_gsi[1],
+ pgm->pgm_gsi[2],
+ pgm->pgm_gsi[3],
+ pgm->pgm_gsi[4],
+ pgm->pgm_gsi[5]);
+ printf ("\t\t\"tsduLength\": %i", g_ntohs(pgm->pgm_tsdu_length));
+}
+
+/* 8.1. Source Path Messages (SPM)
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SPM's Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Trailing Edge Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Leading Edge Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NLA AFI | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Path NLA ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+
+ * | Option Extensions when present ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * NLA = Network Layer Address
+ * NLA AFI = NLA Address Family Indicator: rfc 1700 (ADDRESS FAMILY NUMBERS)
+ * => Path NLA = IP address of last network element
+ */
+
+int
+verify_spm (
+ struct pgm_header* header,
+ char* data,
+ guint len
+ )
+{
+ int retval = 0;
+
+/* truncated packet */
+ if (len < sizeof(struct pgm_spm)) {
+ printf ("\t\"message\": \"SPM: packet length: %i less than minimum SPM length: %" G_GSIZE_FORMAT "lu bytes.\",\n", len, sizeof(struct pgm_spm));
+ retval = -1;
+ goto out;
+ }
+
+ struct pgm_spm* spm = (struct pgm_spm*)data;
+ char* opt_offset = (char*)(spm + 1);
+ guint opt_len = len - sizeof(spm);
+
+ switch (g_ntohs(spm->spm_nla_afi)) {
+ case AFI_IP6:
+ if (len < sizeof(struct pgm_spm6)) {
+ printf ("\t\"message\": \"SPM: packet length: %i less than minimum IPv6 SPM length: %" G_GSIZE_FORMAT "lu bytes.\",\n", len, sizeof(struct pgm_spm6));
+ retval = -1;
+ goto out;
+ }
+ opt_offset += sizeof(struct pgm_spm6) - sizeof(struct pgm_spm);
+ opt_len -= sizeof(struct pgm_spm6) - sizeof(struct pgm_spm);
+
+ case AFI_IP:
+ break;
+
+ default:
+ printf ("\t\"message\": \"SPM: invalid AFI of source NLA: %i.\",\n", g_ntohs(spm->spm_nla_afi));
+ retval = -1;
+ goto out;
+ }
+
+/* option extensions */
+ if (header->pgm_options & PGM_OPT_PRESENT)
+ {
+ retval = verify_options (opt_offset, opt_len);
+ }
+
+out:
+ return retval;
+}
+
+void
+print_spm (
+ struct pgm_header* header,
+ char* data
+ )
+{
+ struct pgm_spm* spm = (struct pgm_spm*)data;
+ struct pgm_spm6* spm6 = (struct pgm_spm6*)data;
+ char* opt_offset = (char*)(spm + 1);
+
+ puts (",");
+ printf ("\t\t\"spmSqn\": %i,\n", g_ntohl(spm->spm_sqn));
+ printf ("\t\t\"spmTrail\": %i,\n", g_ntohl(spm->spm_trail));
+ printf ("\t\t\"spmLead\": %i,\n", g_ntohl(spm->spm_lead));
+ printf ("\t\t\"spmNlaAfi\": %i,\n", g_ntohs (spm->spm_nla_afi));
+
+ char s[INET6_ADDRSTRLEN];
+ switch (g_ntohs(spm->spm_nla_afi)) {
+ case AFI_IP:
+ inet_ntop ( AF_INET, &spm->spm_nla, s, sizeof (s) );
+ break;
+
+ case AFI_IP6:
+ inet_ntop ( AF_INET6, &spm6->spm6_nla, s, sizeof (s) );
+ opt_offset += sizeof(struct pgm_spm6) - sizeof(struct pgm_spm);
+ break;
+ }
+
+ printf ("\t\t\"spmNla\": \"%s\"", s);
+
+/* option extensions */
+ if (header->pgm_options & PGM_OPT_PRESENT)
+ {
+ puts (",");
+ print_options (opt_offset);
+ }
+
+ puts ("\n\t}");
+}
+
+/* 14.7.1. Poll Request
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | POLL's Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | POLL's Round | POLL's Sub-type |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NLA AFI | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Path NLA ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+
+ * | POLL's Back-off Interval |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Random String |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Matching Bit-Mask |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Option Extensions when present ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Sent to ODATA multicast group with IP Router Alert option.
+ */
+
+#define PGM_MIN_POLL_SIZE ( sizeof(struct pgm_poll) )
+
+int
+verify_poll (
+ G_GNUC_UNUSED struct pgm_header* header,
+ G_GNUC_UNUSED char* data,
+ G_GNUC_UNUSED guint len
+ )
+{
+ return -1;
+}
+
+void
+print_poll (
+ G_GNUC_UNUSED struct pgm_header* header,
+ G_GNUC_UNUSED char* data
+ )
+{
+}
+
+/* 14.7.2. Poll Response
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | POLR's Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | POLR's Round | reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Option Extensions when present ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+int
+verify_polr (
+ G_GNUC_UNUSED struct pgm_header* header,
+ G_GNUC_UNUSED char* data,
+ G_GNUC_UNUSED guint len
+ )
+{
+ return -1;
+}
+
+void
+print_polr (
+ G_GNUC_UNUSED struct pgm_header* header,
+ G_GNUC_UNUSED char* data
+ )
+{
+}
+
+/* 8.2. Data Packet
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data Packet Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Trailing Edge Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Option Extensions when present ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ... -+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data ...
+ * +-+-+- ...
+ */
+
+int
+verify_odata (
+ struct pgm_header* header,
+ char* data,
+ guint len
+ )
+{
+ int retval = 0;
+
+ if (len < sizeof(struct pgm_data)) {
+ printf ("\t\"message\": \"ODATA: packet length: %i less than minimum ODATA length: %" G_GSIZE_FORMAT " bytes.\",\n", len, sizeof(struct pgm_data));
+ retval = -1;
+ goto out;
+ }
+
+ char* tsdu = data + sizeof(struct pgm_data);
+ guint tsdu_len = len - sizeof(struct pgm_data);
+ if (header->pgm_options & PGM_OPT_PRESENT)
+ {
+ retval = verify_options (tsdu, tsdu_len);
+
+ guint opt_total_len = g_ntohs( OPTIONS_TOTAL_LEN(tsdu) );
+ tsdu += opt_total_len;
+ tsdu_len -= opt_total_len;
+ }
+
+ if (!retval && g_ntohs(header->pgm_tsdu_length) != tsdu_len) {
+ printf ("\t\"message\": \"ODATA: TSDU truncated expected %i, found %i bytes.\",\n", g_ntohs(header->pgm_tsdu_length), tsdu_len);
+ retval = -1;
+ }
+out:
+ return retval;
+}
+
+void
+print_odata (
+ struct pgm_header* header,
+ char* data
+ )
+{
+ struct pgm_data* odata = (struct pgm_data*)data;
+ char* tsdu = data + sizeof(struct pgm_data);
+
+ puts (",");
+ printf ("\t\t\"odSqn\": %lu,\n", (gulong)g_ntohl(odata->data_sqn));
+ printf ("\t\t\"odTrail\": %lu,\n", (gulong)g_ntohl(odata->data_trail));
+
+/* option extensions */
+ if (header->pgm_options & PGM_OPT_PRESENT)
+ {
+ print_options (tsdu);
+ tsdu += g_ntohs( OPTIONS_TOTAL_LEN(tsdu) );
+ puts (",");
+ }
+
+/* data */
+ printf ("\t\t\"data\": \"");
+ char* end = tsdu + g_ntohs (header->pgm_tsdu_length);
+ while (tsdu < end) {
+ if (isprint(*tsdu))
+ putchar(*tsdu);
+ else
+ putchar('.');
+ tsdu++;
+ }
+
+ puts ("\"");
+ puts ("\t}");
+}
+
+/* 8.2. Repair Data
+ */
+
+int
+verify_rdata (
+ struct pgm_header* header,
+ char* data,
+ guint len
+ )
+{
+ int retval = 0;
+
+ if (len < sizeof(struct pgm_data)) {
+ printf ("\t\"message\": \"RDATA: packet length: %i less than minimum RDATA length: %" G_GSIZE_FORMAT " bytes.\",\n", len, sizeof(struct pgm_data));
+ retval = -1;
+ goto out;
+ }
+
+ char* tsdu = data + sizeof(struct pgm_data);
+ guint tsdu_len = len - sizeof(struct pgm_data);
+ if (header->pgm_options & PGM_OPT_PRESENT)
+ {
+ retval = verify_options (tsdu, tsdu_len);
+
+ guint opt_total_len = g_ntohs( OPTIONS_TOTAL_LEN(tsdu) );
+ tsdu += opt_total_len;
+ tsdu_len -= opt_total_len;
+ }
+
+ if (!retval && g_ntohs(header->pgm_tsdu_length) != tsdu_len) {
+ printf ("\t\"message\": \"RDATA: tsdu truncated expected %i, found %i bytes.\",\n", g_ntohs(header->pgm_tsdu_length), tsdu_len);
+ retval = -1;
+ }
+out:
+ return retval;
+}
+
+void
+print_rdata (
+ struct pgm_header* header,
+ char* data
+ )
+{
+ struct pgm_data* rdata = (struct pgm_data*)data;
+ char* tsdu = data + sizeof(struct pgm_data);
+
+ puts (",");
+ printf ("\t\t\"rdSqn\": %lu,\n", (gulong)g_ntohl(rdata->data_sqn));
+ printf ("\t\t\"rdTrail\": %lu,\n", (gulong)g_ntohl(rdata->data_trail));
+
+/* option extensions */
+ if (header->pgm_options & PGM_OPT_PRESENT)
+ {
+ print_options (tsdu);
+ tsdu += g_ntohs( OPTIONS_TOTAL_LEN(tsdu) );
+ puts (",");
+ }
+
+/* data */
+ printf ("\t\t\"data\": \"");
+ char* end = tsdu + g_ntohs (header->pgm_tsdu_length);
+ while (tsdu < end) {
+ if (isprint(*tsdu))
+ putchar(*tsdu);
+ else
+ putchar('.');
+ tsdu++;
+ }
+
+ puts ("\"");
+ puts ("\t}");
+}
+
+/* 8.3. NAK
+ *
+ * Technically the AFI of the source and multicast group can be different
+ * but that would be very wibbly wobbly. One example is using a local DLR
+ * with a IPv4 address to reduce NAK cost for recovery on wide IPv6
+ * distribution.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Requested Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NLA AFI | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Source NLA ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+
+ * | NLA AFI | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Multicast Group NLA ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+
+ * | Option Extensions when present ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ...
+ */
+
+int
+verify_nak (
+ struct pgm_header* header,
+ char* data,
+ guint len
+ )
+{
+ return generic_verify_nak ("NAK", header, data, len);
+}
+
+int
+verify_ncf (
+ struct pgm_header* header,
+ char* data,
+ guint len
+ )
+{
+ return generic_verify_nak ("NCF", header, data, len);
+}
+
+int
+verify_nnak (
+ struct pgm_header* header,
+ char* data,
+ guint len
+ )
+{
+ return generic_verify_nak ("NNAK", header, data, len);
+}
+
+static int
+generic_verify_nak (
+ const char* name, /* upper case */
+ G_GNUC_UNUSED struct pgm_header* header,
+ char* data,
+ guint len
+ )
+{
+ int retval = 0;
+
+/* truncated packet */
+ if (len < sizeof(struct pgm_nak)) {
+ printf ("\t\"message\": \"%s: packet length: %i less than minimum %s length: %" G_GSIZE_FORMAT " bytes.\",\n",
+ name, len, name, sizeof(struct pgm_nak));
+ retval = -1;
+ goto out;
+ }
+
+ struct pgm_nak* nak = (struct pgm_nak*)data;
+ int nak_src_nla_afi = g_ntohs (nak->nak_src_nla_afi);
+ int nak_grp_nla_afi = -1;
+
+/* check source NLA: unicast address of the ODATA sender */
+ switch (nak_src_nla_afi) {
+ case AFI_IP:
+ nak_grp_nla_afi = g_ntohs (nak->nak_grp_nla_afi);
+ break;
+
+ case AFI_IP6:
+ nak_grp_nla_afi = g_ntohs (((struct pgm_nak6*)nak)->nak6_grp_nla_afi);
+ break;
+
+ default:
+ printf ("\t\"message\": \"%s: invalid AFI of source NLA: %i.\",\n",
+ name, nak_src_nla_afi);
+ retval = -1;
+ goto out;
+ }
+
+/* check multicast group NLA */
+ switch (nak_grp_nla_afi) {
+ case AFI_IP6:
+ switch (nak_src_nla_afi) {
+/* IPv4 + IPv6 NLA */
+ case AFI_IP:
+ if (len < ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )) {
+ printf ("\t\"message\": \"%s: packet length: %i less than joint IPv4/6 %s length: %" G_GSIZE_FORMAT " bytes.\",\n",
+ name, len, name, ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) ));
+ retval = -1;
+ }
+ break;
+
+/* IPv6 + IPv6 NLA */
+ case AFI_IP6:
+ if (len < sizeof(struct pgm_nak6)) {
+ printf ("\t\"message\": \"%s: packet length: %i less than IPv6 %s length: %" G_GSIZE_FORMAT " bytes.\",\n",
+ name, len, name, sizeof(struct pgm_nak6));
+ retval = -1;
+ }
+ break;
+ }
+ break;
+
+ case AFI_IP:
+ if (nak_src_nla_afi == AFI_IP6) {
+ if (len < ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) )) {
+ printf ("\t\"message\": \"%s: packet length: %i less than joint IPv6/4 %s length: %" G_GSIZE_FORMAT " bytes.\",\n",
+ name, len, name, ( sizeof(struct pgm_nak) + sizeof(struct in6_addr) - sizeof(struct in_addr) ));
+ retval = -1;
+ }
+ }
+ break;
+
+ default:
+ printf ("\t\"message\": \"%s: invalid AFI of group NLA: %i.\",\n",
+ name, nak_grp_nla_afi);
+ retval = -1;
+ break;
+ }
+
+out:
+ return retval;
+}
+
+void
+print_nak (
+ struct pgm_header* header,
+ char* data
+ )
+{
+ generic_print_nak ("nak", header, data);
+}
+
+void
+print_ncf (
+ struct pgm_header* header,
+ char* data
+ )
+{
+ generic_print_nak ("ncf", header, data);
+}
+
+void
+print_nnak (
+ struct pgm_header* header,
+ char* data
+ )
+{
+ generic_print_nak ("nnak", header, data);
+}
+
+static void
+generic_print_nak (
+ const char* name, /* lower case */
+ struct pgm_header* header,
+ char* data
+ )
+{
+ struct pgm_nak* nak = (struct pgm_nak*)data;
+ struct pgm_nak6* nak6 = (struct pgm_nak6*)data;
+ char* opt_offset = (char*)(nak + 1);
+
+ puts (",");
+ printf ("\t\t\"%sSqn\": %lu,\n", name, (gulong)g_ntohl(nak->nak_sqn));
+
+ guint16 nak_src_nla_afi = g_ntohs (nak->nak_src_nla_afi);
+ guint16 nak_grp_nla_afi = 0;
+ char s[INET6_ADDRSTRLEN];
+
+ printf ("\t\t\"%sSourceNlaAfi\": %i,\n", name, nak_src_nla_afi);
+
+/* source nla */
+ switch (nak_src_nla_afi) {
+ case AFI_IP:
+ inet_ntop ( AF_INET, &nak->nak_src_nla, s, sizeof(s) );
+ nak_grp_nla_afi = g_ntohs (nak->nak_grp_nla_afi);
+ break;
+
+ case AFI_IP6:
+ inet_ntop ( AF_INET6, &nak6->nak6_src_nla, s, sizeof(s) );
+ nak_grp_nla_afi = g_ntohs (nak6->nak6_grp_nla_afi);
+ opt_offset += sizeof(struct in6_addr) - sizeof(struct in_addr);
+ break;
+ }
+
+ printf ("\t\t\"%sSourceNla\": \"%s\",\n", name, s);
+ printf ("\t\t\"%sGroupNlaAfi\": %i,\n", name, nak_grp_nla_afi);
+
+ switch (nak_grp_nla_afi) {
+ case AFI_IP6:
+ switch (nak_src_nla_afi) {
+/* IPv4 + IPv6 NLA */
+ case AFI_IP:
+ inet_ntop ( AF_INET6, &nak->nak_grp_nla, s, sizeof(s) );
+ break;
+
+/* IPv6 + IPv6 NLA */
+ case AFI_IP6:
+ inet_ntop ( AF_INET6, &nak6->nak6_grp_nla, s, sizeof(s) );
+ break;
+ }
+ opt_offset += sizeof(struct in6_addr) - sizeof(struct in_addr);
+ break;
+
+ case AFI_IP:
+ switch (nak_src_nla_afi) {
+/* IPv4 + IPv4 NLA */
+ case AFI_IP:
+ inet_ntop ( AF_INET, &nak->nak_grp_nla, s, sizeof(s) );
+ break;
+
+/* IPv6 + IPv4 NLA */
+ case AFI_IP6:
+ inet_ntop ( AF_INET, &nak6->nak6_grp_nla, s, sizeof(s) );
+ break;
+ }
+ break;
+ }
+
+ printf ("\t\t\"%sGroupNla\": \"%s\"", name, s);
+
+/* option extensions */
+ if (header->pgm_options & PGM_OPT_PRESENT)
+ {
+ puts (",");
+ print_options (opt_offset);
+ }
+
+ puts ("\n\t}");
+}
+
+
+/* 13.6. SPM Request
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Option Extensions when present ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- ...
+ */
+
+int
+verify_spmr (
+ struct pgm_header* header,
+ char* data,
+ guint len
+ )
+{
+ int retval = 0;
+
+ char* opt_offset = data;
+ guint opt_len = len;
+
+/* option extensions */
+ if (header->pgm_options & PGM_OPT_PRESENT)
+ {
+ retval = verify_options (opt_offset, opt_len);
+ }
+
+ return retval;
+}
+
+void
+print_spmr (
+ struct pgm_header* header,
+ char* data
+ )
+{
+ char* opt_offset = data;
+
+/* option extensions */
+ if (header->pgm_options & PGM_OPT_PRESENT)
+ {
+ print_options (opt_offset);
+ puts ("");
+ }
+
+ puts ("\t}");
+}
+
+/* Parse PGM options fields:
+ *
+ * assume at least two options, one the mandatory OPT_LENGTH
+ */
+
+#define PGM_MIN_OPT_SIZE ( sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_length) )
+
+int
+verify_options (
+ char* data,
+ guint len
+ )
+{
+ int retval = 0;
+
+ if (len < PGM_MIN_OPT_SIZE) {
+ printf ("\t\"message\": \"PGM options: packet size too small for options.\",\n");
+ retval = -1;
+ goto out;
+ }
+
+/* OPT_LENGTH first */
+ struct pgm_opt_length* opt_len = (struct pgm_opt_length*)data;
+ if ((opt_len->opt_type & PGM_OPT_MASK) != PGM_OPT_LENGTH) {
+ printf ("\t\"message\": \"PGM options: first option not OPT_LENGTH.\",\n");
+ retval = -1;
+ goto out;
+ }
+
+ if (opt_len->opt_length != sizeof(struct pgm_opt_length)) {
+ printf ("\t\"message\": \"PGM options: OPT_LENGTH incorrect option length: %i, expecting %" G_GSIZE_FORMAT " bytes.\",\n", opt_len->opt_length, sizeof(struct pgm_opt_length));
+ retval = -1;
+ goto out;
+ }
+
+ if (g_ntohs(opt_len->opt_total_length) < PGM_MIN_OPT_SIZE) {
+ printf ("\t\"message\": \"PGM options: OPT_LENGTH total length too short: %i bytes.\",\n", g_ntohs(opt_len->opt_total_length));
+ retval = -1;
+ goto out;
+ }
+
+ if (g_ntohs(opt_len->opt_total_length) > len) {
+ printf ("\t\"message\": \"PGM options: OPT_LENGTH total length longer than packet allows: %i bytes.\",\n", g_ntohs(opt_len->opt_total_length));
+ retval = -1;
+ goto out;
+ }
+
+/* iterate through options (max 16) */
+ guint count = 0;
+ guint total_length = g_ntohs(opt_len->opt_total_length);
+
+ guint opt_counters[256];
+ memset (&opt_counters, 0, sizeof(opt_counters));
+
+ struct pgm_opt_header* opt_header = (struct pgm_opt_header*)data;
+ for (;;) {
+ total_length -= opt_header->opt_length;
+ if (total_length < sizeof(struct pgm_opt_header)) {
+ printf ("\t\"message\": \"PGM options: option #%i shorter than minimum option size.\",\n", count + 1);
+ retval = -1;
+ goto out;
+ }
+ opt_header = (struct pgm_opt_header*)( ((char*)opt_header) + opt_header->opt_length );
+ if (((int)total_length - (int)opt_header->opt_length) < 0) {
+ printf ("\t\"message\": \"PGM options: option #%i shorter than embedded size.\",\n", count + 1);
+ retval = -1;
+ goto out;
+ }
+
+ if (opt_counters[opt_header->opt_type]++) {
+ printf ("\t\"message\": \"PGM options: duplicate option %i.\",\n", opt_header->opt_type);
+ retval = -1;
+ goto out;
+ }
+
+/* check option types */
+ switch (opt_header->opt_type & PGM_OPT_MASK) {
+ case PGM_OPT_FRAGMENT:
+ {
+ if (opt_header->opt_length != sizeof(struct pgm_opt_header) + sizeof(struct pgm_opt_fragment)) {
+ printf ("\t\"message\": \"PGM options: OPT_FRAGMENT incorrect size: %i bytes.\",\n", opt_header->opt_length);
+ retval = -1;
+ goto out;
+ }
+
+ struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1);
+ if (g_ntohl(opt_fragment->opt_frag_off) > g_ntohl(opt_fragment->opt_frag_len)) {
+ printf ("\t\"message\": \"PGM options: fragment offset longer than original packet.\",\n");
+ retval = -1;
+ goto out;
+ }
+ break;
+ }
+
+ case PGM_OPT_NAK_LIST:
+ {
+ guint list_len = opt_header->opt_length - sizeof(struct pgm_opt_header) - sizeof(guint8);
+ if (list_len & 1) {
+ printf ("\t\"message\": \"PGM options: OPT_NAK_LIST invalid odd length: %i bytes.\",\n", opt_header->opt_length);
+ retval = -1;
+ goto out;
+ }
+
+ list_len /= 2;
+ if (list_len == 0) {
+ printf ("\t\"message\": \"PGM options: OPT_NAK_LIST empty.\",\n");
+ retval = -1;
+ goto out;
+ }
+
+ if (list_len > 62) {
+ printf ("\t\"message\": \"PGM options: OPT_NAK_LIST too long: %i sqns.\",\n", list_len);
+ retval = -1;
+ goto out;
+ }
+ break;
+ }
+
+ case PGM_OPT_PARITY_PRM:
+ {
+ struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1);
+ if ((opt_parity_prm->opt_reserved & PGM_PARITY_PRM_MASK) == 0) {
+ printf ("\t\"message\": \"PGM options: neither pro-active or on-demand parity set in OPT_PARITY_PRM.\",\n");
+ retval = -1;
+ goto out;
+ }
+ guint32 parity_prm_tgs = g_ntohl (opt_parity_prm->parity_prm_tgs);
+ if (parity_prm_tgs < 2 || parity_prm_tgs > 128) {
+ printf ("\t\"message\": \"PGM options: transmission group size out of bounds: %i.\",\n", parity_prm_tgs);
+ retval = -1;
+ goto out;
+ }
+ break;
+ }
+
+ default:
+/* unknown option, skip */
+ break;
+ }
+/* end option types */
+
+ if (opt_header->opt_type & PGM_OPT_END) {
+ break;
+ }
+
+ if (count++ == 16) {
+ printf ("\t\"message\": \"PGM options: more than 16 options found.\",\n");
+ retval = -1;
+ goto out;
+ }
+ }
+
+out:
+ return retval;
+}
+
+static const char *opx_text[4] = {
+ "OPX_IGNORE",
+ "OPX_INVALIDATE",
+ "OPX_DISCARD",
+ "OPX_UNKNOWN"
+};
+
+void
+print_options (
+ char* data
+ )
+{
+ struct pgm_opt_length* opt_len = (struct pgm_opt_length*)data;
+
+ puts ("\t\t\"pgmOptions\": [");
+ puts ("\t\t\t{");
+ printf ("\t\t\t\t\"length\": 0x%x,\n", opt_len->opt_length);
+ puts ("\t\t\t\t\"type\": \"OPT_LENGTH\",");
+ printf ("\t\t\t\t\"totalLength\": %i\n", g_ntohs (opt_len->opt_total_length));
+ printf ("\t\t\t}");
+
+/* iterate through options */
+ struct pgm_opt_header* opt_header = (struct pgm_opt_header*)data;
+ do {
+ opt_header = (struct pgm_opt_header*)( ((char*)opt_header) + opt_header->opt_length );
+
+ puts (",");
+ puts ("\t\t\t{");
+ printf ("\t\t\t\t\"length\": 0x%x,\n", opt_header->opt_length);
+
+ switch (opt_header->opt_type & PGM_OPT_MASK) {
+ case PGM_OPT_FRAGMENT:
+ {
+ struct pgm_opt_fragment* opt_fragment = (struct pgm_opt_fragment*)(opt_header + 1);
+ printf ("\t\t\t\t\"type\": \"OPT_FRAGMENT%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : "");
+ printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false");
+ printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]);
+ printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_fragment->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false");
+ printf ("\t\t\t\t\"firstSqn\": %i,\n", g_ntohl(opt_fragment->opt_sqn));
+ printf ("\t\t\t\t\"fragmentOffset\": %i,\n", g_ntohl(opt_fragment->opt_frag_off));
+ printf ("\t\t\t\t\"originalLength\": %i\n", g_ntohl(opt_fragment->opt_frag_len));
+ break;
+ }
+
+ case PGM_OPT_NAK_LIST:
+ {
+ struct pgm_opt_nak_list* opt_nak_list = (struct pgm_opt_nak_list*)(opt_header + 1);
+ char* end = (char*)opt_header + opt_header->opt_length;
+ printf ("\t\t\t\t\"type\": \"OPT_NAK_LIST%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : "");
+ printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false");
+ printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]);
+ printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_nak_list->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false");
+ char sqns[1024] = "";
+ guint i = 0;
+ do {
+ char sqn[1024];
+ sprintf (sqn, "%s%i", (i>0)?", ":"", g_ntohl(opt_nak_list->opt_sqn[i]));
+ strcat (sqns, sqn);
+ i++;
+ } while ((char*)&opt_nak_list->opt_sqn[i] < end);
+ printf ("\t\t\t\t\"sqn\": [%s]\n", sqns);
+ break;
+ }
+
+ case PGM_OPT_PARITY_PRM:
+ {
+ struct pgm_opt_parity_prm* opt_parity_prm = (struct pgm_opt_parity_prm*)(opt_header + 1);
+ printf ("\t\t\t\t\"type\": \"OPT_PARITY_PRM%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : "");
+ printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false");
+ printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]);
+ printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false");
+ printf ("\t\t\t\t\"P-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_PARITY_PRM_PRO) ? "true" : "false");
+ printf ("\t\t\t\t\"O-bit\": %s,\n", (opt_parity_prm->opt_reserved & PGM_PARITY_PRM_OND) ? "true" : "false");
+ printf ("\t\t\t\t\"transmissionGroupSize\": %i\n", g_ntohl(opt_parity_prm->parity_prm_tgs));
+ break;
+ }
+
+ case PGM_OPT_CURR_TGSIZE:
+ {
+ struct pgm_opt_curr_tgsize* opt_curr_tgsize = (struct pgm_opt_curr_tgsize*)(opt_header + 1);
+ printf ("\t\t\t\t\"type\": \"OPT_CURR_TGSIZE%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : "");
+ printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false");
+ printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]);
+ printf ("\t\t\t\t\"U-bit\": %s,\n", (opt_curr_tgsize->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false");
+ printf ("\t\t\t\t\"actualTransmissionGroupSize\": %i\n", g_ntohl(opt_curr_tgsize->prm_atgsize));
+ break;
+ }
+
+ case PGM_OPT_SYN:
+ {
+ struct pgm_opt_syn* opt_syn = (struct pgm_opt_syn*)(opt_header + 1);
+ printf ("\t\t\t\t\"type\": \"OPT_SYN%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : "");
+ printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false");
+ printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]);
+ printf ("\t\t\t\t\"U-bit\": %s\n", (opt_syn->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false");
+ break;
+ }
+
+ case PGM_OPT_FIN:
+ {
+ struct pgm_opt_fin* opt_fin = (struct pgm_opt_fin*)(opt_header + 1);
+ printf ("\t\t\t\t\"type\": \"OPT_FIN%s\",\n", (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : "");
+ printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false");
+ printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]);
+ printf ("\t\t\t\t\"U-bit\": %s\n", (opt_fin->opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false");
+ break;
+ }
+
+ default:
+ {
+ guint8 opt_reserved = *(guint8*)(opt_header + 1);
+ printf ("\t\t\t\t\"type\": \"0x%x%s\",\n", opt_header->opt_type & PGM_OPT_MASK, (opt_header->opt_type & PGM_OPT_END) ? "|OPT_END" : "");
+ printf ("\t\t\t\t\"F-bit\": %s,\n", (opt_header->opt_reserved & PGM_OP_ENCODED) ? "true" : "false");
+ printf ("\t\t\t\t\"OPX\": \"%s\",\n", opx_text[opt_header->opt_reserved & PGM_OPX_MASK]);
+ printf ("\t\t\t\t\"U-bit\": %s\n", (opt_reserved & PGM_OP_ENCODED_NULL) ? "true" : "false");
+ break;
+ }
+ }
+ printf ("\t\t\t}");
+
+ } while (!(opt_header->opt_type & PGM_OPT_END));
+
+ printf ("\n\t\t]");
+}
+
+/* eof */