summaryrefslogtreecommitdiffstats
path: root/3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c')
-rw-r--r--3rdparty/openpgm-svn-r1085/pgm/examples/pgmtop.c1031
1 files changed, 1031 insertions, 0 deletions
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 */