/*
 * Note: this file originally auto-generated by mib2c using
 *        : mib2c.notify.conf,v 5.3 2004/04/15 12:29:19 dts12 Exp $
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

#include <impl/i18n.h>
#include <impl/framework.h>
#include <impl/receiver.h>
#include <impl/socket.h>

#include "pgm/snmp.h"
#include "impl/pgmMIB.h"
#include "impl/pgmMIB_columns.h"
#include "impl/pgmMIB_enums.h"


//#define PGMMIB_DEBUG


/* locals */

struct pgm_snmp_data_context_t {
	pgm_sock_t*	sock;
	pgm_peer_t*	peer;
};

typedef struct pgm_snmp_data_context_t pgm_snmp_data_context_t;

struct pgm_snmp_context_t {
	pgm_slist_t*	list;
	pgm_list_t*	node;
	int		index;		/* table index */
	unsigned 	instance;	/* unique number per node */
	pgm_snmp_data_context_t	data_context;
};

typedef struct pgm_snmp_context_t pgm_snmp_context_t;


static const oid snmptrap_oid[] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0};


/* functions */

static int initialize_table_pgmSourceTable(void);
static Netsnmp_Node_Handler pgmSourceTable_handler;
static Netsnmp_First_Data_Point pgmSourceTable_get_first_data_point;
static Netsnmp_Next_Data_Point pgmSourceTable_get_next_data_point;
static Netsnmp_Free_Loop_Context pgmSourceTable_free_loop_context;

static int initialize_table_pgmSourceConfigTable(void);
static Netsnmp_Node_Handler pgmSourceConfigTable_handler;
static Netsnmp_First_Data_Point pgmSourceConfigTable_get_first_data_point;
static Netsnmp_Next_Data_Point pgmSourceConfigTable_get_next_data_point;
static Netsnmp_Free_Loop_Context pgmSourceConfigTable_free_loop_context;

static int initialize_table_pgmSourcePerformanceTable(void);
static Netsnmp_Node_Handler pgmSourcePerformanceTable_handler;
static Netsnmp_First_Data_Point pgmSourcePerformanceTable_get_first_data_point;
static Netsnmp_Next_Data_Point pgmSourcePerformanceTable_get_next_data_point;
static Netsnmp_Free_Loop_Context pgmSourcePerformanceTable_free_loop_context;

static int initialize_table_pgmReceiverTable(void);
static Netsnmp_Node_Handler pgmReceiverTable_handler;
static Netsnmp_First_Data_Point pgmReceiverTable_get_first_data_point;
static Netsnmp_Next_Data_Point pgmReceiverTable_get_next_data_point;
static Netsnmp_Free_Loop_Context pgmReceiverTable_free_loop_context;

static int initialize_table_pgmReceiverConfigTable(void);
static Netsnmp_Node_Handler pgmReceiverConfigTable_handler;
static Netsnmp_First_Data_Point pgmReceiverConfigTable_get_first_data_point;
static Netsnmp_Next_Data_Point pgmReceiverConfigTable_get_next_data_point;
static Netsnmp_Free_Loop_Context pgmReceiverConfigTable_free_loop_context;

static int initialize_table_pgmReceiverPerformanceTable(void);
static Netsnmp_Node_Handler pgmReceiverPerformanceTable_handler;
static Netsnmp_First_Data_Point pgmReceiverPerformanceTable_get_first_data_point;
static Netsnmp_Next_Data_Point pgmReceiverPerformanceTable_get_next_data_point;
static Netsnmp_Free_Loop_Context pgmReceiverPerformanceTable_free_loop_context;

PGM_GNUC_INTERNAL
bool
pgm_mib_init (
	pgm_error_t**	error
	)
{
	if (MIB_REGISTERED_OK != initialize_table_pgmSourceTable()) {
		pgm_set_error (error,
			     PGM_ERROR_DOMAIN_SNMP,
			     PGM_ERROR_FAILED,
			     _("pgmSourceTable registration: see SNMP log for further details."));
		return FALSE;
	}
	if (MIB_REGISTERED_OK != initialize_table_pgmSourceConfigTable()) {
		pgm_set_error (error,
			     PGM_ERROR_DOMAIN_SNMP,
			     PGM_ERROR_FAILED,
			     _("pgmSourceConfigTable registration: see SNMP log for further details."));
		return FALSE;
	}
	if (MIB_REGISTERED_OK != initialize_table_pgmSourcePerformanceTable()) {
		pgm_set_error (error,
			     PGM_ERROR_DOMAIN_SNMP,
			     PGM_ERROR_FAILED,
			     _("pgmSourcePerformanceTable registration: see SNMP log for further details."));
		return FALSE;
	}
	if (MIB_REGISTERED_OK != initialize_table_pgmReceiverTable()) {
		pgm_set_error (error,
			     PGM_ERROR_DOMAIN_SNMP,
			     PGM_ERROR_FAILED,
			     _("pgmReceiverTable registration: see SNMP log for further details."));
		return FALSE;
	}
	if (MIB_REGISTERED_OK != initialize_table_pgmReceiverConfigTable()) {
		pgm_set_error (error,
			     PGM_ERROR_DOMAIN_SNMP,
			     PGM_ERROR_FAILED,
			     _("pgmReceiverConfigTable registration: see SNMP log for further details."));
		return FALSE;
	}
	if (MIB_REGISTERED_OK != initialize_table_pgmReceiverPerformanceTable()) {
		pgm_set_error (error,
			     PGM_ERROR_DOMAIN_SNMP,
			     PGM_ERROR_FAILED,
			     _("pgmReceiverPerformanceTable registration: see SNMP log for further details."));
		return FALSE;
	}

	return TRUE;
}

/*
 * pgmSourceTable
 *
 * returns MIB_REGISTERED_OK on success, failures include:
 * 	MIB_REGISTRATION_FAILED
 * 	MIB_DUPLICATE_REGISTRATION
 * 	SNMPERR_GENERR
 */

static
int
initialize_table_pgmSourceTable (void)
{
	pgm_debug ("initialize_table_pgmSourceTable ()");

	static const oid pgmSourceTable_oid[] = {1,3,6,1,3,112,1,2,100,2};
	netsnmp_table_registration_info* table_info = NULL;
	netsnmp_iterator_info* iinfo = NULL;
	netsnmp_handler_registration* reg = NULL;

	reg = netsnmp_create_handler_registration ("pgmSourceTable",	pgmSourceTable_handler,
						   pgmSourceTable_oid,	OID_LENGTH( pgmSourceTable_oid ),
						   HANDLER_CAN_RONLY);
	if (NULL == reg)
		goto error;

	table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
	if (NULL == table_info)
		goto error;

	table_info->min_column = COLUMN_PGMSOURCESOURCEADDRESS;
	table_info->max_column = COLUMN_PGMSOURCESOURCEPORTNUMBER;

	netsnmp_table_helper_add_indexes (table_info,
					  ASN_OCTET_STR,  /* index: pgmSourceGlobalId */
					  ASN_UNSIGNED,  /* index: pgmSourceSourcePort */
					  0);

	iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info );
	if (NULL == iinfo)
		goto error;

	iinfo->get_first_data_point 	= pgmSourceTable_get_first_data_point;
	iinfo->get_next_data_point  	= pgmSourceTable_get_next_data_point;
	iinfo->free_loop_context_at_end = pgmSourceTable_free_loop_context;
	iinfo->table_reginfo        	= table_info;

	return netsnmp_register_table_iterator (reg, iinfo);

error:
	if (table_info && table_info->indexes)		/* table_data_free_func() is internal */
		snmp_free_var (table_info->indexes);
	SNMP_FREE( table_info );
	SNMP_FREE( iinfo );
	netsnmp_handler_registration_free (reg);

	return -1;
}

/* called for first row of data in SNMP table
 *
 * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context,
 * optionally returns my_data_context.
 *
 * returns answer or NULL
 */

static
netsnmp_variable_list*
pgmSourceTable_get_first_data_point(
	void**			my_loop_context,	/* valid through one query of multiple "data points" */
	void**			my_data_context,	/* answer blob which is passed to handler() */
	netsnmp_variable_list*	put_index_data,		/* answer */
	netsnmp_iterator_info*	mydata			/* iinfo on init() */
	)
{
/* pre-conditions */
	pgm_assert (NULL != my_loop_context);
	pgm_assert (NULL != my_data_context);
	pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

	pgm_debug ("pgmSourceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
		(const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_rwlock_reader_lock (&pgm_sock_list_lock);

	if (NULL == pgm_sock_list) {
		pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
		return NULL;
	}

/* create our own context for this SNMP loop */
	pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1);
	context->list = pgm_sock_list;
	*my_loop_context = context;

/* pass on for generic row access */
	return pgmSourceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata);
}

static
netsnmp_variable_list*
pgmSourceTable_get_next_data_point(
	void**			my_loop_context,
	void**			my_data_context,
	netsnmp_variable_list*	put_index_data,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
	pgm_assert (NULL != my_loop_context);
	pgm_assert (NULL != my_data_context);
	pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

	pgm_debug ("pgmSourceTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
		(const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context;
	netsnmp_variable_list *idx = put_index_data;

	if (NULL == context->list)
		return NULL;

	pgm_sock_t* sock = context->list->data;

/* pgmSourceGlobalId */
	char gsi[ PGM_GSISTRLEN ];
	pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi));
	snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi));
	idx = idx->next_variable;

/* pgmSourceSourcePort */
	const unsigned sport = ntohs (sock->tsi.sport);
	snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport));

	*my_data_context = sock;
	context->list = context->list->next;

	return put_index_data;
}

static
void
pgmSourceTable_free_loop_context (
	void*			my_loop_context,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
	pgm_assert (NULL != my_loop_context);
	pgm_assert (NULL != mydata);

	pgm_debug ("pgmSourceTable_free_loop_context (my_loop_context:%p mydata:%p)",
		(const void*)my_loop_context,
		(const void*)mydata);

	pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context;
	pgm_free (context);
	my_loop_context = NULL;

	pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
}

static
int
pgmSourceTable_handler (
	netsnmp_mib_handler*		handler,
	netsnmp_handler_registration*	reginfo,
	netsnmp_agent_request_info*	reqinfo,
	netsnmp_request_info*		requests
	)
{
/* pre-conditions */
	pgm_assert (NULL != handler);
	pgm_assert (NULL != reginfo);
	pgm_assert (NULL != reqinfo);
	pgm_assert (NULL != requests);

	pgm_debug ("pgmSourceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)",
		(const void*)handler,
		(const void*)reginfo,
		(const void*)reqinfo,
		(const void*)requests);
	
	switch (reqinfo->mode) {

/* Read-support (also covers GetNext requests) */

	case MODE_GET:
		for (netsnmp_request_info* request = requests;
		     request;
		     request = request->next)
		{
			const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request);
			if (NULL == sock) {
				netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE);
				continue;
			}

			netsnmp_variable_list *var = request->requestvb;
			netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request);
			if (NULL == table_info) {
				snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n");
				continue;
			}

			switch (table_info->colnum) {

			case COLUMN_PGMSOURCESOURCEADDRESS:
				{
					struct sockaddr_in s4;
					if (AF_INET == sock->send_gsr.gsr_source.ss_family)
						memcpy (&s4, &sock->send_gsr.gsr_source, sizeof(s4));
					else
						memset (&s4, 0, sizeof(s4));
					snmp_set_var_typed_value (var, ASN_IPADDRESS,
								  (const u_char*)&s4.sin_addr.s_addr,
								  sizeof(struct in_addr) );
				}
				break;

			case COLUMN_PGMSOURCEGROUPADDRESS:
				{
					struct sockaddr_in s4;
					if (AF_INET == sock->send_gsr.gsr_group.ss_family)
						memcpy (&s4, &sock->send_gsr.gsr_group, sizeof(s4));
					else
						memset (&s4, 0, sizeof(s4));
					snmp_set_var_typed_value (var, ASN_IPADDRESS,
								  (const u_char*)&s4.sin_addr.s_addr,
								  sizeof(struct in_addr) );
				}
				break;

			case COLUMN_PGMSOURCEDESTPORT:
				{
					const unsigned dport = ntohs (sock->dport);
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&dport, sizeof(dport) );
				}
				break;

/* copy index[0] */
			case COLUMN_PGMSOURCESOURCEGSI:
				snmp_set_var_typed_value (var, ASN_OCTET_STR,
							  (const u_char*)table_info->indexes->val.string,
							  table_info->indexes->val_len);
				break;

/* copy index[1] */
			case COLUMN_PGMSOURCESOURCEPORTNUMBER:
				snmp_set_var_typed_value (var, ASN_UNSIGNED,
							  (const u_char*)table_info->indexes->next_variable->val.integer,
							  table_info->indexes->next_variable->val_len);
			
				break;

			default:
				snmp_log (LOG_ERR, "pgmSourceTable_handler: unknown column.\n");
				break;
			}
		}
		break;

	case MODE_SET_RESERVE1:
	default:
		snmp_log (LOG_ERR, "pgmSourceTable_handler: unsupported mode.\n");
		break;

	}

	return SNMP_ERR_NOERROR;
}

/*
 * pgmSourceConfigTable
 *
 */

static
int
initialize_table_pgmSourceConfigTable(void)
{
	pgm_debug ("initialize_table_pgmSourceConfigTable ()");

	static const oid pgmSourceConfigTable_oid[] = {1,3,6,1,3,112,1,2,100,3};
	netsnmp_table_registration_info* table_info = NULL;
	netsnmp_iterator_info* iinfo = NULL;
	netsnmp_handler_registration* reg = NULL;

	reg = netsnmp_create_handler_registration ("pgmSourceConfigTable",	pgmSourceConfigTable_handler,
						   pgmSourceConfigTable_oid,	OID_LENGTH( pgmSourceConfigTable_oid ),
						   HANDLER_CAN_RONLY);
	if (NULL == reg)
		goto error;

	table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
	if (NULL == table_info)
		goto error;

	table_info->min_column = COLUMN_PGMSOURCETTL;
	table_info->max_column = COLUMN_PGMSOURCESPMPATHADDRESS;

	netsnmp_table_helper_add_indexes (table_info,
					  ASN_OCTET_STR,  /* index: pgmSourceConfigGlobalId */
					  ASN_UNSIGNED,  /* index: pgmSourceConfigSourcePort */
					  0);

	iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info );
	if (NULL == iinfo)
		goto error;

	iinfo->get_first_data_point 	= pgmSourceConfigTable_get_first_data_point;
	iinfo->get_next_data_point 	= pgmSourceConfigTable_get_next_data_point;
	iinfo->free_loop_context_at_end = pgmSourceConfigTable_free_loop_context;
	iinfo->table_reginfo        	= table_info;

	return netsnmp_register_table_iterator (reg, iinfo);

error:
	if (table_info && table_info->indexes)		/* table_data_free_func() is internal */
		snmp_free_var (table_info->indexes);
	SNMP_FREE( table_info );
	SNMP_FREE( iinfo );
	netsnmp_handler_registration_free (reg);

	return -1;
}

/* called for first row of data in SNMP table
 *
 * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context,
 * optionally returns my_data_context.
 *
 * returns answer or NULL
 */

static
netsnmp_variable_list*
pgmSourceConfigTable_get_first_data_point(
	void**			my_loop_context,	/* valid through one query of multiple "data points" */
	void**			my_data_context,	/* answer blob which is passed to handler() */
	netsnmp_variable_list*	put_index_data,		/* answer */
	netsnmp_iterator_info*	mydata			/* iinfo on init() */
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
        pgm_assert (NULL != my_data_context);
        pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmSourceConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_rwlock_reader_lock (&pgm_sock_list_lock);

	if (NULL == pgm_sock_list) {
		pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
		return NULL;
	}

/* create our own context for this SNMP loop */
	pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1);
	context->list = pgm_sock_list;
	*my_loop_context = context;

/* pass on for generic row access */
	return pgmSourceConfigTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata);
}

static
netsnmp_variable_list*
pgmSourceConfigTable_get_next_data_point(
	void**			my_loop_context,
	void**			my_data_context,
	netsnmp_variable_list*	put_index_data,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
        pgm_assert (NULL != my_data_context);
        pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmSourceConfigTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context;
	netsnmp_variable_list *idx = put_index_data;

	if (NULL == context->list)
		return NULL;

	pgm_sock_t* sock = context->list->data;

/* pgmSourceGlobalId */
	char gsi[ PGM_GSISTRLEN ];
	pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi));
	snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi));
	idx = idx->next_variable;

/* pgmSourceSourcePort */
	const unsigned sport = ntohs (sock->tsi.sport);
	snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport));

	*my_data_context = sock;
	context->list = context->list->next;

	return put_index_data;
}

static
void
pgmSourceConfigTable_free_loop_context (
	void*			my_loop_context,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmSourceConfigTable_free_loop_context (my_loop_context:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)mydata);

	pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context;
	pgm_free (context);
	my_loop_context = NULL;

	pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
}

static
int
pgmSourceConfigTable_handler (
	netsnmp_mib_handler*		handler,
	netsnmp_handler_registration*	reginfo,
	netsnmp_agent_request_info*	reqinfo,
	netsnmp_request_info*		requests
	)
{
/* pre-conditions */
        pgm_assert (NULL != handler);
        pgm_assert (NULL != reginfo);
        pgm_assert (NULL != reqinfo);
        pgm_assert (NULL != requests);

        pgm_debug ("pgmSourceConfigTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)",
                (const void*)handler,
		(const void*)reginfo,
		(const void*)reqinfo,
		(const void*)requests);

	switch (reqinfo->mode) {

/* Read-support (also covers GetNext requests) */

	case MODE_GET:
		for (netsnmp_request_info* request = requests;
		     request;
		     request = request->next)
		{
			const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request);
			if (NULL == sock) {
				netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE);
				continue;
			}

			netsnmp_variable_list *var = request->requestvb;
			netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request);
			if (NULL == table_info) {
				snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n");
				continue;
			}

			switch (table_info->colnum) {

			case COLUMN_PGMSOURCETTL:
				{
					const unsigned hops = sock->hops;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&hops, sizeof(hops) );
				}
				break;

			case COLUMN_PGMSOURCEADVMODE:
				{
					const unsigned adv_mode = 0 == sock->adv_mode ? PGMSOURCEADVMODE_TIME : PGMSOURCEADVMODE_DATA;
					snmp_set_var_typed_value (var, ASN_INTEGER,
								  (const u_char*)&adv_mode, sizeof(adv_mode) );
				}
				break;

/* FIXED: pgmSourceLateJoin = disable(2) */
			case COLUMN_PGMSOURCELATEJOIN:
				{
					const unsigned late_join = PGMSOURCELATEJOIN_DISABLE;
					snmp_set_var_typed_value (var, ASN_INTEGER,
								  (const u_char*)&late_join, sizeof(late_join) );
				}
				break;

			case COLUMN_PGMSOURCETXWMAXRTE:
				{
					const unsigned txw_max_rte = sock->txw_max_rte;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&txw_max_rte, sizeof(txw_max_rte) );
				}
				break;

			case COLUMN_PGMSOURCETXWSECS:
				{
					const unsigned txw_secs = sock->txw_secs;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&txw_secs, sizeof(txw_secs) );
				}
				break;

/* FIXED: TXW_ADV_SECS = 0 */
			case COLUMN_PGMSOURCETXWADVSECS:
				{
					const unsigned txw_adv_secs = 0;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&txw_adv_secs, sizeof(txw_adv_secs) );
				}
				break;

/* FIXED: pgmSourceAdvIvl = TXW_ADV_SECS * 1000 = 0 */
			case COLUMN_PGMSOURCEADVIVL:
				{
					const unsigned adv_ivl = 0;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&adv_ivl, sizeof(adv_ivl) );
				}
				break;

			case COLUMN_PGMSOURCESPMIVL:
				{
					const unsigned spm_ivl = pgm_to_msecs (sock->spm_ambient_interval);
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&spm_ivl, sizeof(spm_ivl) );
				}
				break;

/* TODO: IHB_MIN */
			case COLUMN_PGMSOURCESPMHEARTBEATIVLMIN:
				{
					const unsigned ihb_min = 0;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&ihb_min, sizeof(ihb_min) );
				}
				break;

/* TODO: IHB_MAX */
			case COLUMN_PGMSOURCESPMHEARTBEATIVLMAX:
				{
					const unsigned ihb_max = 0;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&ihb_max, sizeof(ihb_max) );
				}
				break;

/* NAK_BO_IVL */
			case COLUMN_PGMSOURCERDATABACKOFFIVL:
				{
					const unsigned nak_bo_ivl = pgm_to_msecs (sock->nak_bo_ivl);
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&nak_bo_ivl, sizeof(nak_bo_ivl) );
				}
				break;

/* FIXED: pgmSourceFEC = disabled(1) */
			case COLUMN_PGMSOURCEFEC:
				{
					const unsigned fec = (sock->use_ondemand_parity || sock->use_proactive_parity) ? 1 : 0;
					snmp_set_var_typed_value (var, ASN_INTEGER,
								  (const u_char*)&fec, sizeof(fec) );
				}
				break;

/* FIXED: pgmSourceFECTransmissionGrpSize = 0 */
			case COLUMN_PGMSOURCEFECTRANSMISSIONGRPSIZE:
				{
					const unsigned fec_tgs = sock->rs_k;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&fec_tgs, sizeof(fec_tgs) );
				}
				break;

/* FIXED: pgmSourceFECProactiveParitySize = 0 */
			case COLUMN_PGMSOURCEFECPROACTIVEPARITYSIZE:
				{
					const unsigned fec_paps = sock->rs_proactive_h;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&fec_paps, sizeof(fec_paps) );
				}
				break;

/* IPv6 not supported */
			case COLUMN_PGMSOURCESPMPATHADDRESS:
				{
					struct sockaddr_in s4;
					if (AF_INET == sock->recv_gsr[0].gsr_source.ss_family)
						memcpy (&s4, &sock->recv_gsr[0].gsr_source, sizeof(s4));
					else
						memset (&s4, 0, sizeof(s4));
					snmp_set_var_typed_value (var, ASN_IPADDRESS,
								  (const u_char*)&s4.sin_addr.s_addr,
								  sizeof(struct in_addr) );
				}
				break;

			default:
				snmp_log (LOG_ERR, "pgmSourceConfigTable_handler: unknown column.\n");
				break;
			}
		}
		break;

	case MODE_SET_RESERVE1:
	default:
		snmp_log (LOG_ERR, "pgmSourceConfigTable_handler: unsupported mode.\n");
		break;

	}

	return SNMP_ERR_NOERROR;
}

/*
 * pgmSourcePerformanceTable
 */

static
int
initialize_table_pgmSourcePerformanceTable (void)
{
	pgm_debug ("initialize_table_pgmSourcePerformanceTable ()");

	static const oid pgmSourcePerformanceTable_oid[] = {1,3,6,1,3,112,1,2,100,4};
	netsnmp_table_registration_info* table_info = NULL;
	netsnmp_iterator_info* iinfo = NULL;
	netsnmp_handler_registration* reg = NULL;

	reg = netsnmp_create_handler_registration ("pgmSourcePerformanceTable",		pgmSourcePerformanceTable_handler,
						   pgmSourcePerformanceTable_oid,	OID_LENGTH( pgmSourcePerformanceTable_oid ),
						   HANDLER_CAN_RONLY);
	if (NULL == reg)
		goto error;

	table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
	if (NULL == table_info)
		goto error;

	table_info->min_column = COLUMN_PGMSOURCEDATABYTESSENT;
	table_info->max_column = COLUMN_PGMSOURCENNAKERRORS;

	netsnmp_table_helper_add_indexes (table_info,
					  ASN_OCTET_STR,  /* index: pgmSourceGlobalId */
					  ASN_UNSIGNED,  /* index: pgmSourceSourcePort */
					  0);

	iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info );
	if (NULL == iinfo)
		goto error;

	iinfo->get_first_data_point 	= pgmSourcePerformanceTable_get_first_data_point;
	iinfo->get_next_data_point  	= pgmSourcePerformanceTable_get_next_data_point;
	iinfo->free_loop_context_at_end = pgmSourcePerformanceTable_free_loop_context;
	iinfo->table_reginfo        	= table_info;

	return netsnmp_register_table_iterator (reg, iinfo);

error:
	if (table_info && table_info->indexes)		/* table_data_free_func() is internal */
		snmp_free_var (table_info->indexes);
	SNMP_FREE( table_info );
	SNMP_FREE( iinfo );
	netsnmp_handler_registration_free (reg);

	return -1;
}

/* called for first row of data in SNMP table
 *
 * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context,
 * optionally returns my_data_context.
 *
 * returns answer or NULL
 */

static
netsnmp_variable_list*
pgmSourcePerformanceTable_get_first_data_point (
	void**			my_loop_context,	/* valid through one query of multiple "data points" */
	void**			my_data_context,	/* answer blob which is passed to handler() */
	netsnmp_variable_list*	put_index_data,		/* answer */
	netsnmp_iterator_info*	mydata			/* iinfo on init() */
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
        pgm_assert (NULL != my_data_context);
        pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmSourcePerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_rwlock_reader_lock (&pgm_sock_list_lock);

	if (NULL == pgm_sock_list) {
		pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
		return NULL;
	}

/* create our own context for this SNMP loop */
	pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1);
	context->list = pgm_sock_list;
	*my_loop_context = context;

/* pass on for generic row access */
	return pgmSourcePerformanceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata);
}

static
netsnmp_variable_list*
pgmSourcePerformanceTable_get_next_data_point (
	void**			my_loop_context,
	void**			my_data_context,
	netsnmp_variable_list*	put_index_data,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
        pgm_assert (NULL != my_data_context);
        pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmSourcePerformanceTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context;
	netsnmp_variable_list *idx = put_index_data;

	if (NULL == context->list)
		return NULL;

	pgm_sock_t* sock = context->list->data;

/* pgmSourceGlobalId */
	char gsi[ PGM_GSISTRLEN ];
	pgm_gsi_print_r (&sock->tsi.gsi, gsi, sizeof(gsi));
	snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi));
	idx = idx->next_variable;

/* pgmSourceSourcePort */
	const unsigned sport = ntohs (sock->tsi.sport);
	snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport));

	*my_data_context = sock;
	context->list = context->list->next;

	return put_index_data;
}

static
void
pgmSourcePerformanceTable_free_loop_context (
	void*			my_loop_context,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmPerformanceSourceTable_free_loop_context (my_loop_context:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)mydata);
 
	pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context;
	pgm_free (context);
	my_loop_context = NULL;

	pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
}

static
int
pgmSourcePerformanceTable_handler (
	netsnmp_mib_handler*		handler,
	netsnmp_handler_registration*	reginfo,
	netsnmp_agent_request_info*	reqinfo,
	netsnmp_request_info*		requests
	)
{
/* pre-conditions */
        pgm_assert (NULL != handler);
        pgm_assert (NULL != reginfo);
        pgm_assert (NULL != reqinfo);
        pgm_assert (NULL != requests);

        pgm_debug ("pgmSourcePerformanceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)",
                (const void*)handler,
		(const void*)reginfo,
		(const void*)reqinfo,
		(const void*)requests);

	switch (reqinfo->mode) {

/* Read-support (also covers GetNext requests) */

	case MODE_GET:
		for (netsnmp_request_info* request = requests;
		     request;
		     request = request->next)
		{
			const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context (request);
			if (NULL == sock) {
				netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE);
				continue;
			}

			const pgm_txw_t* window = (const pgm_txw_t*)sock->window;

			netsnmp_variable_list *var = request->requestvb;
			netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request);
			if (NULL == table_info) {
				snmp_log (LOG_ERR, "pgmSourceTable_handler: empty table request info.\n");
				continue;
			}

			switch (table_info->colnum) {

			case COLUMN_PGMSOURCEDATABYTESSENT:
				{
					const unsigned data_bytes = sock->cumulative_stats[PGM_PC_SOURCE_DATA_BYTES_SENT];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&data_bytes, sizeof(data_bytes) );
				}
				break;

			case COLUMN_PGMSOURCEDATAMSGSSENT:
				{
					const unsigned data_msgs = sock->cumulative_stats[PGM_PC_SOURCE_DATA_MSGS_SENT];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&data_msgs, sizeof(data_msgs) );
				}
				break;

			case COLUMN_PGMSOURCEBYTESBUFFERED:
				{
					const unsigned bytes_buffered = sock->can_send_data ? pgm_txw_size (window) : 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&bytes_buffered, sizeof(bytes_buffered) );
				}
				break;

			case COLUMN_PGMSOURCEMSGSBUFFERED:
				{
					const unsigned msgs_buffered = sock->can_send_data ? pgm_txw_length (window) : 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&msgs_buffered, sizeof(msgs_buffered) );
				}
				break;

/* PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED + COLUMN_PGMSOURCEPARITYBYTESRETRANSMITTED */
			case COLUMN_PGMSOURCEBYTESRETRANSMITTED:
				{
					const unsigned bytes_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&bytes_resent, sizeof(bytes_resent) );
				}
				break;

/* PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED + COLUMN_PGMSOURCEPARITYMSGSRETRANSMITTED */
			case COLUMN_PGMSOURCEMSGSRETRANSMITTED:
				{
					const unsigned msgs_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&msgs_resent, sizeof(msgs_resent) );
				}
				break;

			case COLUMN_PGMSOURCEBYTESSENT:
				{
					const unsigned bytes_sent = sock->cumulative_stats[PGM_PC_SOURCE_BYTES_SENT];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&bytes_sent, sizeof(bytes_sent) );
				}
				break;

/* COLUMN_PGMSOURCEPARITYNAKPACKETSRECEIVED + COLUMN_PGMSOURCESELECTIVENAKPACKETSRECEIVED */
			case COLUMN_PGMSOURCERAWNAKSRECEIVED:
				{
					const unsigned nak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&nak_packets, sizeof(nak_packets) );
				}
				break;

/* PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED + COLUMN_PGMSOURCEPARITYNAKSIGNORED */
			case COLUMN_PGMSOURCENAKSIGNORED:
				{
					const unsigned naks_ignored = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&naks_ignored, sizeof(naks_ignored) );
				}
				break;

			case COLUMN_PGMSOURCECKSUMERRORS:
				{
					const unsigned cksum_errors = sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&cksum_errors, sizeof(cksum_errors) );
				}
				break;

			case COLUMN_PGMSOURCEMALFORMEDNAKS:
				{
					const unsigned malformed_naks = sock->cumulative_stats[PGM_PC_SOURCE_MALFORMED_NAKS];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&malformed_naks, sizeof(malformed_naks) );
				}
				break;

			case COLUMN_PGMSOURCEPACKETSDISCARDED:
				{
					const unsigned packets_discarded = sock->cumulative_stats[PGM_PC_SOURCE_PACKETS_DISCARDED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&packets_discarded, sizeof(packets_discarded) );
				}
				break;

/* PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED + COLUMN_PGMSOURCEPARITYNAKSRECEIVED */
			case COLUMN_PGMSOURCENAKSRCVD:
				{
					const unsigned naks_received = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&naks_received, sizeof(naks_received) );
				}
				break;

/* FIXED: 0 */
			case COLUMN_PGMSOURCEPARITYBYTESRETRANSMITTED:
				{
					const unsigned parity_bytes_resent = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&parity_bytes_resent, sizeof(parity_bytes_resent) );
				}
				break;

			case COLUMN_PGMSOURCESELECTIVEBYTESRETRANSMITED:
				{
					const unsigned selective_bytes_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_BYTES_RETRANSMITTED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&selective_bytes_resent, sizeof(selective_bytes_resent) );
				}
				break;

/* FIXED: 0 */
			case COLUMN_PGMSOURCEPARITYMSGSRETRANSMITTED:
				{
					const unsigned parity_msgs_resent = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&parity_msgs_resent, sizeof(parity_msgs_resent) );
				}
				break;

			case COLUMN_PGMSOURCESELECTIVEMSGSRETRANSMITTED:
				{
					const unsigned selective_msgs_resent = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_MSGS_RETRANSMITTED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&selective_msgs_resent, sizeof(selective_msgs_resent) );
				}
				break;

/* FIXED: 0 */
			case COLUMN_PGMSOURCEBYTESADMIT:
				{
					const unsigned bytes_admit = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&bytes_admit, sizeof(bytes_admit) );
				}
				break;

/* FIXED: 0 */
			case COLUMN_PGMSOURCEMSGSADMIT:
				{
					const unsigned msgs_admit = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&msgs_admit, sizeof(msgs_admit) );
				}
				break;

/* FIXED: 0 */
			case COLUMN_PGMSOURCEPARITYNAKPACKETSRECEIVED:
				{
					const unsigned parity_nak_packets = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&parity_nak_packets, sizeof(parity_nak_packets) );
				}
				break;

			case COLUMN_PGMSOURCESELECTIVENAKPACKETSRECEIVED:
				{
					const unsigned selective_nak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&selective_nak_packets, sizeof(selective_nak_packets) );
				}
				break;

/* FIXED: 0 */
			case COLUMN_PGMSOURCEPARITYNAKSRECEIVED:
				{
					const unsigned parity_naks = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&parity_naks, sizeof(parity_naks) );
				}
				break;

			case COLUMN_PGMSOURCESELECTIVENAKSRECEIVED:
				{
					const unsigned selective_naks = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&selective_naks, sizeof(selective_naks) );
				}
				break;

/* FIXED: 0 */
			case COLUMN_PGMSOURCEPARITYNAKSIGNORED:
				{
					const unsigned parity_naks_ignored = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&parity_naks_ignored, sizeof(parity_naks_ignored) );
				}
				break;

			case COLUMN_PGMSOURCESELECTIVENAKSIGNORED:
				{
					const unsigned selective_naks_ignored = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NAKS_IGNORED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&selective_naks_ignored, sizeof(selective_naks_ignored) );
				}
				break;

			case COLUMN_PGMSOURCEACKERRORS:
				{
					const unsigned ack_errors = sock->cumulative_stats[PGM_PC_SOURCE_ACK_ERRORS];;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&ack_errors, sizeof(ack_errors) );
				}
				break;

			case COLUMN_PGMSOURCEPGMCCACKER:
				{
					struct sockaddr_in s4;
					if (AF_INET == sock->acker_nla.ss_family)
						memcpy (&s4, &sock->acker_nla, sizeof(s4));
					else
						memset (&s4, 0, sizeof(s4));
					snmp_set_var_typed_value (var, ASN_IPADDRESS,
								  (const u_char*)&s4.sin_addr.s_addr,
								  sizeof(struct in_addr) );
				}
				break;

			case COLUMN_PGMSOURCETRANSMISSIONCURRENTRATE:
				{
					const unsigned tx_current_rate = sock->cumulative_stats[PGM_PC_SOURCE_TRANSMISSION_CURRENT_RATE];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&tx_current_rate, sizeof(tx_current_rate) );
				}
				break;

			case COLUMN_PGMSOURCEACKPACKETSRECEIVED:
				{
					const unsigned ack_packets = sock->cumulative_stats[PGM_PC_SOURCE_ACK_PACKETS_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&ack_packets, sizeof(ack_packets) );
				}
				break;

/* COLUMN_PGMSOURCEPARITYNNAKPACKETSRECEIVED + COLUMN_PGMSOURCESELECTIVENNAKPACKETSRECEIVED */
			case COLUMN_PGMSOURCENNAKPACKETSRECEIVED:
				{
					const unsigned nnak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&nnak_packets, sizeof(nnak_packets) );
				}
				break;

/* FIXED: 0 */
			case COLUMN_PGMSOURCEPARITYNNAKPACKETSRECEIVED:
				{
					const unsigned parity_nnak_packets = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&parity_nnak_packets, sizeof(parity_nnak_packets) );
				}
				break;

			case COLUMN_PGMSOURCESELECTIVENNAKPACKETSRECEIVED:
				{
					const unsigned selective_nnak_packets = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAK_PACKETS_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&selective_nnak_packets, sizeof(selective_nnak_packets) );
				}
				break;

/* COLUMN_PGMSOURCEPARITYNNAKSRECEIVED + COLUMN_PGMSOURCESELECTIVENNAKSRECEIVED */
			case COLUMN_PGMSOURCENNAKSRECEIVED:
				{
					const unsigned nnaks_received = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&nnaks_received, sizeof(nnaks_received) );
				}
				break;

/* FIXED: 0 */
			case COLUMN_PGMSOURCEPARITYNNAKSRECEIVED:
				{
					const unsigned parity_nnaks = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&parity_nnaks, sizeof(parity_nnaks) );
				}
				break;

			case COLUMN_PGMSOURCESELECTIVENNAKSRECEIVED:
				{
					const unsigned selective_nnaks = sock->cumulative_stats[PGM_PC_SOURCE_SELECTIVE_NNAKS_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&selective_nnaks, sizeof(selective_nnaks) );
				}
				break;

			case COLUMN_PGMSOURCENNAKERRORS:
				{
					const unsigned malformed_nnaks = sock->cumulative_stats[PGM_PC_SOURCE_NNAK_ERRORS];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&malformed_nnaks, sizeof(malformed_nnaks) );
				}
				break;

			default:
				snmp_log (LOG_ERR, "pgmSourcePerformanceTable_handler: unknown column.\n");
				break;
			}
		}
		break;

	case MODE_SET_RESERVE1:
	default:
		snmp_log (LOG_ERR, "pgmSourcePerformanceTable_handler: unsupported mode.\n");
		break;

	}

	return SNMP_ERR_NOERROR;
}

/*
 * pgmReceiverTable
 */

static
int
initialize_table_pgmReceiverTable(void)
{
	pgm_debug ("initialize_table_pgmReceiverTable ()");

	static const oid pgmReceiverTable_oid[] = {1,3,6,1,3,112,1,3,100,2};
	netsnmp_table_registration_info* table_info = NULL;
	netsnmp_iterator_info* iinfo = NULL;
	netsnmp_handler_registration* reg = NULL;

	reg = netsnmp_create_handler_registration ("pgmReceiverTable",		pgmReceiverTable_handler,
						   pgmReceiverTable_oid,	OID_LENGTH( pgmReceiverTable_oid ),
						   HANDLER_CAN_RONLY);
	if (NULL == reg)
		goto error;

	table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
	if (NULL == table_info)
		goto error;

	table_info->min_column = COLUMN_PGMRECEIVERGROUPADDRESS;
	table_info->max_column = COLUMN_PGMRECEIVERUNIQUEINSTANCE;

	netsnmp_table_helper_add_indexes (table_info,
					  ASN_OCTET_STR,  /* index: pgmReceiverGlobalId */
					  ASN_UNSIGNED,  /* index: pgmReceiverSourcePort */
					  ASN_UNSIGNED,  /* index: pgmReceiverInstance */
					  0);

	iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info );
	if (NULL == iinfo)
		goto error;

	iinfo->get_first_data_point 	= pgmReceiverTable_get_first_data_point;
	iinfo->get_next_data_point  	= pgmReceiverTable_get_next_data_point;
	iinfo->free_loop_context_at_end = pgmReceiverTable_free_loop_context;
	iinfo->table_reginfo        	= table_info;

	return netsnmp_register_table_iterator (reg, iinfo);

error:
	if (table_info && table_info->indexes)		/* table_data_free_func() is internal */
		snmp_free_var (table_info->indexes);
	SNMP_FREE( table_info );
	SNMP_FREE( iinfo );
	netsnmp_handler_registration_free (reg);

	return -1;
}

/* called for first row of data in SNMP table
 *
 * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context,
 * optionally returns my_data_context.
 *
 * returns answer or NULL
 */

static
netsnmp_variable_list*
pgmReceiverTable_get_first_data_point (
	void**			my_loop_context,	/* valid through one query of multiple "data points" */
	void**			my_data_context,	/* answer blob which is passed to handler() */
	netsnmp_variable_list*	put_index_data,		/* answer */
	netsnmp_iterator_info*	mydata			/* iinfo on init() */
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
        pgm_assert (NULL != my_data_context);
        pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmReceiverTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_rwlock_reader_lock (&pgm_sock_list_lock);

	if (NULL == pgm_sock_list) {
		pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
		return NULL;
	}

/* create our own context for this SNMP loop */
	pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1);

/* hunt to find first node, through all socks */
	for (context->list = pgm_sock_list;
	     context->list;
	     context->list = context->list->next)
	{
/* and through all peers for each sock */
		pgm_sock_t* sock = (pgm_sock_t*)context->list->data;
		pgm_rwlock_reader_lock (&sock->peers_lock);
		context->node = sock->peers_list;
		if (context->node) {
/* maintain this sock's peers lock */
			break;
		}

		pgm_rwlock_reader_unlock (&sock->peers_lock);
	}

/* no node found */
	if (NULL == context->node) {
		pgm_free (context);
		pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
		return NULL;
	}

	*my_loop_context = context; 
	*my_data_context = &context->data_context;

/* pass on for generic row access */
	return pgmReceiverTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata);
}

static
netsnmp_variable_list*
pgmReceiverTable_get_next_data_point (
	void**			my_loop_context,
	void**			my_data_context,
	netsnmp_variable_list*	put_index_data,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
        pgm_assert (NULL != my_data_context);
        pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmReceiverTable_get_next_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context;
	pgm_snmp_data_context_t* data_context = (pgm_snmp_data_context_t*)*my_data_context;
	netsnmp_variable_list *idx = put_index_data;

	if (NULL == context->list)
		return NULL;

	pgm_sock_t* sock = context->list->data;
	data_context->sock = sock;

	if (NULL == context->node)
		return NULL;

	pgm_peer_t* peer = context->node->data;
	data_context->peer = peer;

/* pgmReceiverGlobalId */
	char gsi[ PGM_GSISTRLEN ];
	pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi));
	snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi));
	idx = idx->next_variable;

/* pgmReceiverSourcePort */
	const unsigned sport = ntohs (peer->tsi.sport);
	snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport));
	idx = idx->next_variable;

/* pgmReceiverInstance */
	const unsigned instance = context->instance++;
	snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance));

/* hunt for next valid node */
	if (context->node->next) {
		context->node = context->node->next;
	}
	else
	{
		context->node = NULL;
		while (context->list->next) {
			pgm_rwlock_reader_unlock (&sock->peers_lock);
			context->list = context->list->next;
			sock = context->list->data;
			pgm_rwlock_reader_lock (&sock->peers_lock);
			context->node = sock->peers_list;
			if (context->node) {
/* keep lock */
				break;
			}
		}
	}

	return put_index_data;
}

static
void
pgmReceiverTable_free_loop_context (
	void*			my_loop_context,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmReceiverTable_free_loop_context (my_loop_context:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)mydata);

	pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context;

/* check for intra-peer state */
	if (context->list) {
		pgm_sock_t* sock = context->list->data;
		pgm_rwlock_reader_unlock (&sock->peers_lock);
	}

	pgm_free (context);
	my_loop_context = NULL;

	pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
}

static
int
pgmReceiverTable_handler (
	netsnmp_mib_handler*		handler,
	netsnmp_handler_registration*	reginfo,
	netsnmp_agent_request_info*	reqinfo,
	netsnmp_request_info*		requests
	)
{
/* pre-conditions */
        pgm_assert (NULL != handler);
        pgm_assert (NULL != reginfo);
        pgm_assert (NULL != reqinfo);
        pgm_assert (NULL != requests);

        pgm_debug ("pgmReceiverTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)",
                (const void*)handler,
		(const void*)reginfo,
		(const void*)reqinfo,
		(const void*)requests);

	switch (reqinfo->mode) {

/* Read-support (also covers GetNext requests) */

	case MODE_GET:
		for (netsnmp_request_info* request = requests;
		     request;
		     request = request->next)
		{
			const pgm_snmp_data_context_t* data_context = (pgm_snmp_data_context_t*)netsnmp_extract_iterator_context (request);
			if (!data_context) {
				netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE);
				continue;
			}

			const pgm_sock_t* sock = data_context->sock;
			const pgm_peer_t* peer = data_context->peer;

			netsnmp_variable_list *var = request->requestvb;
			netsnmp_table_request_info* table_info = netsnmp_extract_table_info(request);

			if (table_info == NULL) {
				snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n");
				continue;
			}

			switch (table_info->colnum) {

			case COLUMN_PGMRECEIVERGROUPADDRESS:
				{
					struct sockaddr_in s4;
					if (AF_INET == peer->group_nla.ss_family)
						memcpy (&s4, &peer->group_nla, sizeof(s4));
					else
						memset (&s4, 0, sizeof(s4));
					snmp_set_var_typed_value (var, ASN_IPADDRESS,
								  (const u_char*)&s4.sin_addr.s_addr,
								  sizeof(struct in_addr) );
				}
				break;

/* by definition same as sock */
			case COLUMN_PGMRECEIVERDESTPORT:
				{
					const unsigned dport = ntohs (sock->dport);
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&dport, sizeof(dport) );
				}
				break;

			case COLUMN_PGMRECEIVERSOURCEADDRESS:
				{
					struct sockaddr_in s4;
					if (AF_INET == peer->nla.ss_family)
						memcpy (&s4, &peer->nla, sizeof(s4));
					else
						memset (&s4, 0, sizeof(s4));
					snmp_set_var_typed_value (var, ASN_IPADDRESS,
								  (const u_char*)&s4.sin_addr.s_addr,
								  sizeof(struct in_addr) );
				}
				break;

			case COLUMN_PGMRECEIVERLASTHOP:
				{
					struct sockaddr_in s4;
					if (AF_INET == peer->local_nla.ss_family)
						memcpy (&s4, &peer->local_nla, sizeof(s4));
					else
						memset (&s4, 0, sizeof(s4));
					snmp_set_var_typed_value (var, ASN_IPADDRESS,
								  (const u_char*)&s4.sin_addr.s_addr,
								  sizeof(struct in_addr) );
				}
				break;

/* copy index[0] */
			case COLUMN_PGMRECEIVERSOURCEGSI:
				snmp_set_var_typed_value (var, ASN_OCTET_STR,
							  (const u_char*)table_info->indexes->val.string,
							  table_info->indexes->val_len);
				break;

/* copy index[1] */
			case COLUMN_PGMRECEIVERSOURCEPORTNUMBER:
				snmp_set_var_typed_value (var, ASN_UNSIGNED,
							  (const u_char*)table_info->indexes->next_variable->val.integer,
							  table_info->indexes->next_variable->val_len);
				break;

/* copy index[2] */
			case COLUMN_PGMRECEIVERUNIQUEINSTANCE:
				snmp_set_var_typed_value (var, ASN_UNSIGNED,
							  (const u_char*)table_info->indexes->next_variable->next_variable->val.integer,
							  table_info->indexes->next_variable->next_variable->val_len);
				break;

			default:
				snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n");
				break;
			}
		}
		break;

	case MODE_SET_RESERVE1:
	default:
		snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n");
		break;

	}

	return SNMP_ERR_NOERROR;
}

/*
 * pgmReceiverConfigTable
 *
 */

static
int
initialize_table_pgmReceiverConfigTable(void)
{
	pgm_debug ("initialize_table_pgmReceiverConfigTable ()");

	static const oid pgmReceiverConfigTable_oid[] = {1,3,6,1,3,112,1,3,100,3};
	netsnmp_table_registration_info* table_info = NULL;
	netsnmp_iterator_info* iinfo = NULL;
	netsnmp_handler_registration* reg = NULL;

	reg = netsnmp_create_handler_registration ("pgmReceiverConfigTable",	pgmReceiverConfigTable_handler,
						   pgmReceiverConfigTable_oid,	OID_LENGTH(pgmReceiverConfigTable_oid),
						   HANDLER_CAN_RONLY);
	if (NULL == reg)
		goto error;

	table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
	if (NULL == table_info)
		goto error;

	table_info->min_column = COLUMN_PGMRECEIVERNAKBACKOFFIVL;
	table_info->max_column = COLUMN_PGMRECEIVERNAKFAILURETHRESHOLD;

	netsnmp_table_helper_add_indexes (table_info,
					  ASN_OCTET_STR, /* index: pgmReceiverConfigGlobalId */
					  ASN_UNSIGNED,  /* index: pgmReceiverConfigSourcePort */
					  ASN_UNSIGNED,  /* index: pgmReceiverInstance */
					  0);

	iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info );
	if (NULL == iinfo)
		goto error;

	iinfo->get_first_data_point 	= pgmReceiverConfigTable_get_first_data_point;
	iinfo->get_next_data_point  	= pgmReceiverConfigTable_get_next_data_point;
	iinfo->free_loop_context_at_end = pgmReceiverConfigTable_free_loop_context;
	iinfo->table_reginfo        	= table_info;

	return netsnmp_register_table_iterator (reg, iinfo);

error:
	if (table_info && table_info->indexes)		/* table_data_free_func() is internal */
		snmp_free_var (table_info->indexes);
	SNMP_FREE( table_info );
	SNMP_FREE( iinfo );
	netsnmp_handler_registration_free (reg);

	return -1;
}

/* called for first row of data in SNMP table
 *
 * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context,
 * optionally returns my_data_context.
 *
 * returns answer or NULL
 */

static
netsnmp_variable_list*
pgmReceiverConfigTable_get_first_data_point(
	void**			my_loop_context,	/* valid through one query of multiple "data points" */
	void**			my_data_context,	/* answer blob which is passed to handler() */
	netsnmp_variable_list*	put_index_data,		/* answer */
	netsnmp_iterator_info*	mydata			/* iinfo on init() */
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
        pgm_assert (NULL != my_data_context);
        pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmReceiverConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_rwlock_reader_lock (&pgm_sock_list_lock);

	if (NULL == pgm_sock_list) {
		pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
		return NULL;
	}

/* create our own context for this SNMP loop */
	pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1);

/* hunt to find first node, through all socks */
	for (context->list = pgm_sock_list;
	     context->list;
	     context->list = context->list->next)
	{
/* and through all peers for each sock */
		pgm_sock_t* sock = (pgm_sock_t*)context->list->data;
		pgm_rwlock_reader_lock (&sock->peers_lock);
		context->node = sock->peers_list;
		if (context->node)
			break;

		pgm_rwlock_reader_unlock (&sock->peers_lock);
	}

/* no node found */
	if (NULL == context->node) {
		pgm_free (context);
		pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
		return NULL;
	}

	*my_loop_context = context; 
	*my_data_context = NULL;

/* pass on for generic row access */
	return pgmReceiverConfigTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata);
}

static
netsnmp_variable_list*
pgmReceiverConfigTable_get_next_data_point(
	void**			my_loop_context,
	void**			my_data_context,
	netsnmp_variable_list*	put_index_data,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
        pgm_assert (NULL != my_data_context);
        pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmReceiverConfigTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context;
	netsnmp_variable_list *idx = put_index_data;

	if (NULL == context->list)
		return NULL;

	pgm_sock_t* sock = context->list->data;
	*my_data_context = sock;

	if (NULL == context->node)
		return NULL;

	pgm_peer_t* peer = context->node->data;

/* pgmReceiverGlobalId */
	char gsi[ PGM_GSISTRLEN ];
	pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi));
	snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi));
	idx = idx->next_variable;

/* pgmReceiverSourcePort */
	const unsigned sport = ntohs (peer->tsi.sport);
	snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport));
	idx = idx->next_variable;

/* pgmReceiverInstance */
	const unsigned instance = context->instance++;
	snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance));

/* hunt for next valid node */
	if (context->node->next) {
		context->node = context->node->next;
	}
	else
	{
		context->node = NULL;
		while (context->list->next) {
			pgm_rwlock_reader_unlock (&sock->peers_lock);
			context->list = context->list->next;
			sock = context->list->data;
			pgm_rwlock_reader_lock (&sock->peers_lock);
			context->node = sock->peers_list;
			if (context->node) {
/* keep lock */
				break;
			}
		}
	}

	return put_index_data;
}

static
void
pgmReceiverConfigTable_free_loop_context (
	void*			my_loop_context,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmReceiverConfigTable_free_loop_context (my_loop_context:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)mydata);

	pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context;

/* check for intra-peer state */
	if (context->list) {
		pgm_sock_t* sock = context->list->data;
		pgm_rwlock_reader_unlock (&sock->peers_lock);
	}

	pgm_free (context);
	my_loop_context = NULL;

	pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
}

static
int
pgmReceiverConfigTable_handler (
	netsnmp_mib_handler*		handler,
	netsnmp_handler_registration*	reginfo,
	netsnmp_agent_request_info*	reqinfo,
	netsnmp_request_info*		requests
	)
{
/* pre-conditions */
        pgm_assert (NULL != handler);
        pgm_assert (NULL != reginfo);
        pgm_assert (NULL != reqinfo);
        pgm_assert (NULL != requests);

        pgm_debug ("pgmReceiverConfigTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)",
                (const void*)handler,
		(const void*)reginfo,
		(const void*)reqinfo,
		(const void*)requests);

	switch (reqinfo->mode) {

/* Read-support (also covers GetNext requests) */

	case MODE_GET:
		for (netsnmp_request_info* request = requests;
		     request;
		     request = request->next)
		{
			const pgm_sock_t* sock = (pgm_sock_t*)netsnmp_extract_iterator_context(request);
			if (NULL == sock) {
				netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE);
				continue;
			}

			netsnmp_variable_list *var = request->requestvb;
			netsnmp_table_request_info* table_info = netsnmp_extract_table_info(request);

			if (NULL == table_info) {
				snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n");
				continue;
			}

			switch (table_info->colnum) {

/* nak_bo_ivl from sock */
			case COLUMN_PGMRECEIVERNAKBACKOFFIVL:
				{
					const unsigned nak_bo_ivl = sock->nak_bo_ivl;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&nak_bo_ivl, sizeof(nak_bo_ivl) );
				}
				break;

/* nak_rpt_ivl from sock */
			case COLUMN_PGMRECEIVERNAKREPEATIVL:
				{
					const unsigned nak_rpt_ivl = sock->nak_rpt_ivl;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&nak_rpt_ivl, sizeof(nak_rpt_ivl) );
				}
				break;

/* nak_ncf_retries from sock */
			case COLUMN_PGMRECEIVERNAKNCFRETRIES:
				{
					const unsigned nak_ncf_retries = sock->nak_ncf_retries;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&nak_ncf_retries, sizeof(nak_ncf_retries) );
				}
				break;

/* nak_rdata_ivl from sock */
			case COLUMN_PGMRECEIVERNAKRDATAIVL:
				{
					const unsigned nak_rdata_ivl = sock->nak_rdata_ivl;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&nak_rdata_ivl, sizeof(nak_rdata_ivl) );
				}
				break;

/* nak_data_retries from sock */
			case COLUMN_PGMRECEIVERNAKDATARETRIES:
				{
					const unsigned nak_data_retries = sock->nak_data_retries;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&nak_data_retries, sizeof(nak_data_retries) );
				}
				break;

/* FIXED: pgmReceiverSendNaks = enabled(1) */
			case COLUMN_PGMRECEIVERSENDNAKS:
				{
					const unsigned send_naks = PGMRECEIVERSENDNAKS_ENABLED;
					snmp_set_var_typed_value (var, ASN_INTEGER,
								  (const u_char*)&send_naks, sizeof(send_naks) );
				}
				break;

/* FIXED: pgmReceiverLateJoin = disabled(2) */
			case COLUMN_PGMRECEIVERLATEJOIN:
				{
					const unsigned late_join = PGMRECEIVERLATEJOIN_DISABLED;
					snmp_set_var_typed_value (var, ASN_INTEGER,
								  (const u_char*)&late_join, sizeof(late_join) );
				}
				break;

/* FIXED: 1 for multicast */
			case COLUMN_PGMRECEIVERNAKTTL:
				{
					const unsigned nak_hops = 1;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&nak_hops, sizeof(nak_hops) );
				}
				break;

/* FIXED: pgmReceiverDeliveryOrder = ordered(2) */
			case COLUMN_PGMRECEIVERDELIVERYORDER:
				{
					const unsigned delivery_order = PGMRECEIVERDELIVERYORDER_ORDERED;
					snmp_set_var_typed_value (var, ASN_INTEGER,
								  (const u_char*)&delivery_order, sizeof(delivery_order) );
				}
				break;

/* FIXED: pgmReceiverMcastNaks = disabled(2) */
			case COLUMN_PGMRECEIVERMCASTNAKS:
				{
					const unsigned mcast_naks = PGMRECEIVERMCASTNAKS_DISABLED;
					snmp_set_var_typed_value (var, ASN_INTEGER,
								  (const u_char*)&mcast_naks, sizeof(mcast_naks) );
				}
				break;

/* TODO: traps */
			case COLUMN_PGMRECEIVERNAKFAILURETHRESHOLDTIMER:
			case COLUMN_PGMRECEIVERNAKFAILURETHRESHOLD:
				{
					const unsigned threshold = 0;
					snmp_set_var_typed_value (var, ASN_UNSIGNED,
								  (const u_char*)&threshold, sizeof(threshold) );
				}
				break;

			default:
				snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n");
				break;
			}
		}
		break;

	case MODE_SET_RESERVE1:
	default:
		snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n");
		break;

	}

	return SNMP_ERR_NOERROR;
}

/*
 * pgmReceiverPerformanceTable
 */

static
int
initialize_table_pgmReceiverPerformanceTable (void)
{
	pgm_debug ("initialize_table_pgmReceiverPerformanceTable ()");

	static const oid pgmReceiverPerformanceTable_oid[] = {1,3,6,1,3,112,1,3,100,4};
	netsnmp_table_registration_info* table_info = NULL;
	netsnmp_iterator_info* iinfo = NULL;
	netsnmp_handler_registration* reg = NULL;

	reg = netsnmp_create_handler_registration ("pgmReceiverPerformanceTable",	pgmReceiverPerformanceTable_handler,
						   pgmReceiverPerformanceTable_oid,	OID_LENGTH( pgmReceiverPerformanceTable_oid ),
						   HANDLER_CAN_RONLY);
	if (NULL == reg)
		goto error;

	table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
	if (NULL == table_info)
		goto error;

	table_info->min_column = COLUMN_PGMRECEIVERDATABYTESRECEIVED;
	table_info->max_column = COLUMN_PGMRECEIVERLASTINTERVALNAKFAILURES;

	netsnmp_table_helper_add_indexes (table_info,
					  ASN_OCTET_STR,  /* index: pgmReceiverGlobalId */
					  ASN_UNSIGNED,  /* index: pgmReceiverSourcePort */
					  ASN_UNSIGNED,  /* index: pgmReceiverInstance */
					  0);

	iinfo = SNMP_MALLOC_TYPEDEF( netsnmp_iterator_info );
	if (NULL == iinfo)
		goto error;

	iinfo->get_first_data_point 	= pgmReceiverPerformanceTable_get_first_data_point;
	iinfo->get_next_data_point  	= pgmReceiverPerformanceTable_get_next_data_point;
	iinfo->free_loop_context_at_end = pgmReceiverPerformanceTable_free_loop_context;
	iinfo->table_reginfo        	= table_info;

	return netsnmp_register_table_iterator (reg, iinfo);

error:
	if (table_info && table_info->indexes)		/* table_data_free_func() is internal */
		snmp_free_var (table_info->indexes);
	SNMP_FREE( table_info );
	SNMP_FREE( iinfo );
	netsnmp_handler_registration_free (reg);

	return -1;
}

/* called for first row of data in SNMP table
 *
 * goal is to cache all the relevant data for subsequent get_next_data_point (row) calls in my_loop_context,
 * optionally returns my_data_context.
 *
 * returns answer or NULL
 */

static
netsnmp_variable_list*
pgmReceiverPerformanceTable_get_first_data_point (
	void**			my_loop_context,	/* valid through one query of multiple "data points" */
	void**			my_data_context,	/* answer blob which is passed to handler() */
	netsnmp_variable_list*	put_index_data,		/* answer */
	netsnmp_iterator_info*	mydata			/* iinfo on init() */
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
        pgm_assert (NULL != my_data_context);
        pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmReceiverPerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_rwlock_reader_lock (&pgm_sock_list_lock);

	if (NULL == pgm_sock_list) {
		pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
		return NULL;
	}

/* create our own context for this SNMP loop */
	pgm_snmp_context_t* context = pgm_new0 (pgm_snmp_context_t, 1);

/* hunt to find first node, through all socks */
	for (context->list = pgm_sock_list;
	     context->list;
	     context->list = context->list->next)
	{
/* and through all peers for each sock */
		pgm_sock_t* sock = (pgm_sock_t*)context->list->data;
		pgm_rwlock_reader_lock (&sock->peers_lock);
		context->node = sock->peers_list;
		if (context->node)
			break;

		pgm_rwlock_reader_unlock (&sock->peers_lock);
	}

/* no node found */
	if (NULL == context->node) {
		pgm_free (context);
		pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
		return NULL;
	}

	*my_loop_context = context; 
	*my_data_context = &context->data_context;

/* pass on for generic row access */
	return pgmReceiverPerformanceTable_get_next_data_point (my_loop_context, my_data_context, put_index_data, mydata);
}

static
netsnmp_variable_list*
pgmReceiverPerformanceTable_get_next_data_point (
	void**			my_loop_context,
	void**			my_data_context,
	netsnmp_variable_list*	put_index_data,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
        pgm_assert (NULL != my_data_context);
        pgm_assert (NULL != put_index_data);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmReceiverPerformanceTable_get_first_data_point (my_loop_context:%p my_data_context:%p put_index_data:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)my_data_context,
		(const void*)put_index_data,
		(const void*)mydata);

	pgm_snmp_context_t* context = (pgm_snmp_context_t*)*my_loop_context;
	pgm_snmp_data_context_t* data_context = (pgm_snmp_data_context_t*)*my_data_context;
	netsnmp_variable_list *idx = put_index_data;

	if (NULL == context->list)
		return NULL;

	pgm_sock_t* sock = context->list->data;
	data_context->sock = sock;

	if (NULL == context->node)
		return NULL;

	pgm_peer_t* peer = context->node->data;
	data_context->peer = peer;

/* pgmReceiverGlobalId */
	char gsi[ PGM_GSISTRLEN ];
	pgm_gsi_print_r (&peer->tsi.gsi, gsi, sizeof(gsi));
	snmp_set_var_typed_value (idx, ASN_OCTET_STR, (const u_char*)&gsi, strlen (gsi));
	idx = idx->next_variable;

/* pgmReceiverSourcePort */
	const unsigned sport = ntohs (peer->tsi.sport);
	snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&sport, sizeof(sport));
	idx = idx->next_variable;

/* pgmReceiverInstance */
	const unsigned instance = context->instance++;
	snmp_set_var_typed_value (idx, ASN_UNSIGNED, (const u_char*)&instance, sizeof(instance));

/* hunt for next valid node */
	if (context->node->next) {
		context->node = context->node->next;
	}
	else
	{
		context->node = NULL;
		while (context->list->next) {
			pgm_rwlock_reader_unlock (&sock->peers_lock);
			context->list = context->list->next;
			sock = context->list->data;
			pgm_rwlock_reader_lock (&sock->peers_lock);
			context->node = sock->peers_list;

			if (context->node)
				break;
		}
	}

	return put_index_data;
}

static
void
pgmReceiverPerformanceTable_free_loop_context (
	void*			my_loop_context,
	netsnmp_iterator_info*	mydata
	)
{
/* pre-conditions */
        pgm_assert (NULL != my_loop_context);
	pgm_assert (NULL != mydata);

        pgm_debug ("pgmReceiverPerformanceTable_free_loop_context (my_loop_context:%p mydata:%p)",
                (const void*)my_loop_context,
		(const void*)mydata);

	pgm_snmp_context_t* context = (pgm_snmp_context_t*)my_loop_context;

/* check for intra-peer state */
	if (context->list) {
		pgm_sock_t* sock = context->list->data;
		pgm_rwlock_reader_unlock (&sock->peers_lock);
	}

	pgm_free (context);
	my_loop_context = NULL;

	pgm_rwlock_reader_unlock (&pgm_sock_list_lock);
}

static
int
pgmReceiverPerformanceTable_handler (
	netsnmp_mib_handler*		handler,
	netsnmp_handler_registration*	reginfo,
	netsnmp_agent_request_info*	reqinfo,
	netsnmp_request_info*		requests
	)
{
/* pre-conditions */
        pgm_assert (NULL != handler);
        pgm_assert (NULL != reginfo);
        pgm_assert (NULL != reqinfo);
        pgm_assert (NULL != requests);

        pgm_debug ("pgmReceiverPerformanceTable_handler (handler:%p reginfo:%p reqinfo:%p requests:%p)",
                (const void*)handler,
		(const void*)reginfo,
		(const void*)reqinfo,
		(const void*)requests);

	switch (reqinfo->mode) {

/* Read-support (also covers GetNext requests) */

	case MODE_GET:
		for (netsnmp_request_info* request = requests;
		     request;
		     request = request->next)
		{
			const pgm_snmp_data_context_t* data_context = (pgm_snmp_data_context_t*)netsnmp_extract_iterator_context (request);
			if (NULL == data_context) {
				netsnmp_set_request_error (reqinfo, request, SNMP_NOSUCHINSTANCE);
				continue;
			}

			const pgm_sock_t* sock = data_context->sock;
			const pgm_peer_t* peer = data_context->peer;
			const pgm_rxw_t* window = peer->window;

			netsnmp_variable_list *var = request->requestvb;
			netsnmp_table_request_info* table_info = netsnmp_extract_table_info (request);

			if (NULL == table_info) {
				snmp_log (LOG_ERR, "pgmReceiverTable_handler: empty table request info.\n");
				continue;
			}

			switch (table_info->colnum) {

			case COLUMN_PGMRECEIVERDATABYTESRECEIVED:
				{
					const unsigned data_bytes = peer->cumulative_stats[PGM_PC_RECEIVER_DATA_BYTES_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&data_bytes, sizeof(data_bytes) );
				}
				break;
		
			case COLUMN_PGMRECEIVERDATAMSGSRECEIVED:
				{
					const unsigned data_msgs = peer->cumulative_stats[PGM_PC_RECEIVER_DATA_MSGS_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&data_msgs, sizeof(data_msgs) );
				}
				break;

/* total */
			case COLUMN_PGMRECEIVERNAKSSENT:
				{
					const unsigned naks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&naks_sent, sizeof(naks_sent) );
				}
				break;
	
/* total */	
			case COLUMN_PGMRECEIVERNAKSRETRANSMITTED:
				{
					const unsigned naks_resent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&naks_resent, sizeof(naks_resent) );
				}
				break;
	
/* total */	
			case COLUMN_PGMRECEIVERNAKFAILURES:
				{
					const unsigned nak_failures = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&nak_failures, sizeof(nak_failures) );
				}
				break;
		
			case COLUMN_PGMRECEIVERBYTESRECEIVED:
				{
					const unsigned bytes_received = peer->cumulative_stats[PGM_PC_RECEIVER_BYTES_RECEIVED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&bytes_received, sizeof(bytes_received) );
				}
				break;
	
/* total */	
			case COLUMN_PGMRECEIVERNAKSSUPPRESSED:
				{
					const unsigned naks_suppressed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&naks_suppressed, sizeof(naks_suppressed) );
				}
				break;
	
/* bogus: same as source checksum errors */	
			case COLUMN_PGMRECEIVERCKSUMERRORS:
				{
					const unsigned cksum_errors = sock->cumulative_stats[PGM_PC_SOURCE_CKSUM_ERRORS];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&cksum_errors, sizeof(cksum_errors) );
				}
				break;
		
			case COLUMN_PGMRECEIVERMALFORMEDSPMS:
				{
					const unsigned malformed_spms = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_SPMS];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&malformed_spms, sizeof(malformed_spms) );
				}
				break;
		
			case COLUMN_PGMRECEIVERMALFORMEDODATA:
				{
					const unsigned malformed_odata = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_ODATA];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&malformed_odata, sizeof(malformed_odata) );
				}
				break;
		
			case COLUMN_PGMRECEIVERMALFORMEDRDATA:
				{
					const unsigned malformed_rdata = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_RDATA];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&malformed_rdata, sizeof(malformed_rdata) );
				}
				break;
		
			case COLUMN_PGMRECEIVERMALFORMEDNCFS:
				{
					const unsigned malformed_ncfs = peer->cumulative_stats[PGM_PC_RECEIVER_MALFORMED_NCFS];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&malformed_ncfs, sizeof(malformed_ncfs) );
				}
				break;
		
			case COLUMN_PGMRECEIVERPACKETSDISCARDED:
				{
					const unsigned packets_discarded = peer->cumulative_stats[PGM_PC_RECEIVER_PACKETS_DISCARDED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&packets_discarded, sizeof(packets_discarded) );
				}
				break;
		
			case COLUMN_PGMRECEIVERLOSSES:
				{
					const unsigned losses = window->cumulative_losses;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&losses, sizeof(losses) );
				}
				break;
		
			case COLUMN_PGMRECEIVERBYTESDELIVEREDTOAPP:
				{
					const unsigned bytes_delivered = window->bytes_delivered;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&bytes_delivered, sizeof(bytes_delivered) );
				}
				break;
		
			case COLUMN_PGMRECEIVERMSGSDELIVEREDTOAPP:
				{
					const unsigned msgs_delivered = window->msgs_delivered;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&msgs_delivered, sizeof(msgs_delivered) );
				}
				break;
		
			case COLUMN_PGMRECEIVERDUPSPMS:
				{
					const unsigned dup_spms = peer->cumulative_stats[PGM_PC_RECEIVER_DUP_SPMS];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&dup_spms, sizeof(dup_spms) );
				}
				break;
		
			case COLUMN_PGMRECEIVERDUPDATAS:
				{
					const unsigned dup_data = peer->cumulative_stats[PGM_PC_RECEIVER_DUP_DATAS];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&dup_data, sizeof(dup_data) );
				}
				break;
	
/* FIXED: 0 */	
			case COLUMN_PGMRECEIVERDUPPARITIES:
				{
					const unsigned dup_parity = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&dup_parity, sizeof(dup_parity) );
				}
				break;
	
/* COLUMN_PGMRECEIVERPARITYNAKPACKETSSENT + COLUMN_PGMRECEIVERSELECTIVENAKPACKETSSENT */	
			case COLUMN_PGMRECEIVERNAKPACKETSSENT:
				{
					const unsigned nak_packets = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&nak_packets, sizeof(nak_packets) );
				}
				break;
	
/* FIXED: 0 */	
			case COLUMN_PGMRECEIVERPARITYNAKPACKETSSENT:
				{
					const unsigned parity_naks = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&parity_naks, sizeof(parity_naks) );
				}
				break;
		
			case COLUMN_PGMRECEIVERSELECTIVENAKPACKETSSENT:
				{
					const unsigned nak_packets = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAK_PACKETS_SENT];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&nak_packets, sizeof(nak_packets) );
				}
				break;
	
/* FIXED: 0 */	
			case COLUMN_PGMRECEIVERPARITYNAKSSENT:
				{
					const unsigned parity_naks = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&parity_naks, sizeof(parity_naks) );
				}
				break;
		
			case COLUMN_PGMRECEIVERSELECTIVENAKSSENT:
				{
					const unsigned naks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SENT];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&naks_sent, sizeof(naks_sent) );
				}
				break;
	
/* FIXED: 0 */	
			case COLUMN_PGMRECEIVERPARITYNAKSRETRANSMITTED:
				{
					const unsigned parity_resent = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&parity_resent, sizeof(parity_resent) );
				}
				break;
		
			case COLUMN_PGMRECEIVERSELECTIVENAKSRETRANSMITTED:
				{
					const unsigned naks_resent = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_RETRANSMITTED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&naks_resent, sizeof(naks_resent) );
				}
				break;
	
/* COLUMN_PGMRECEIVERPARITYNAKSFAILED + COLUMN_PGMRECEIVERSELECTIVENAKSFAILED */	
			case COLUMN_PGMRECEIVERNAKSFAILED:
				{
					const unsigned naks_failed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&naks_failed, sizeof(naks_failed) );
				}
				break;
	
/* FIXED: 0 */	
			case COLUMN_PGMRECEIVERPARITYNAKSFAILED:
				{
					const unsigned parity_failed = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&parity_failed, sizeof(parity_failed) );
				}
				break;
		
			case COLUMN_PGMRECEIVERSELECTIVENAKSFAILED:
				{
					const unsigned naks_failed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_FAILED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&naks_failed, sizeof(naks_failed) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKSFAILEDRXWADVANCED:
				{
					const unsigned rxw_failed = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_RXW_ADVANCED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&rxw_failed, sizeof(rxw_failed) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKSFALEDNCFRETRIESEXCEEDED:
				{
					const unsigned ncf_retries = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_NCF_RETRIES_EXCEEDED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&ncf_retries, sizeof(ncf_retries) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKSFAILEDDATARETRIESEXCEEDED:
				{
					const unsigned data_retries = peer->cumulative_stats[PGM_PC_RECEIVER_NAKS_FAILED_DATA_RETRIES_EXCEEDED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&data_retries, sizeof(data_retries) );
				}
				break;
	
/* FIXED: 0 - absolutely no idea what this means */	
			case COLUMN_PGMRECEIVERNAKSFAILEDGENEXPIRED:
				{
					const unsigned happy_pandas = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&happy_pandas, sizeof(happy_pandas) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKFAILURESDELIVERED:
				{
					const unsigned delivered = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAILURES_DELIVERED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&delivered, sizeof(delivered) );
				}
				break;
	
/* FIXED: 0 */	
			case COLUMN_PGMRECEIVERPARITYNAKSSUPPRESSED:
				{
					const unsigned suppressed = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&suppressed, sizeof(suppressed) );
				}
				break;
		
			case COLUMN_PGMRECEIVERSELECTIVENAKSSUPPRESSED:
				{
					const unsigned suppressed = peer->cumulative_stats[PGM_PC_RECEIVER_SELECTIVE_NAKS_SUPPRESSED];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&suppressed, sizeof(suppressed) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKERRORS:
				{
					const unsigned malformed_naks = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_ERRORS];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&malformed_naks, sizeof(malformed_naks) );
				}
				break;
	
/* FIXED: 0 */	
			case COLUMN_PGMRECEIVEROUTSTANDINGPARITYNAKS:
				{
					const unsigned outstanding_parity = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&outstanding_parity, sizeof(outstanding_parity) );
				}
				break;
		
			case COLUMN_PGMRECEIVEROUTSTANDINGSELECTIVENAKS:
				{
					const unsigned outstanding_selective = window->nak_backoff_queue.length +
										window->wait_ncf_queue.length +
										window->wait_data_queue.length;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&outstanding_selective, sizeof(outstanding_selective) );
				}
				break;
		
			case COLUMN_PGMRECEIVERLASTACTIVITY:
				{
					union {
						unsigned	uint_value;
						time_t  	time_t_value;
					} last_activity;
					pgm_time_since_epoch (&peer->last_packet, &last_activity.time_t_value);
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&last_activity.uint_value, sizeof(last_activity.uint_value) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKSVCTIMEMIN:
				{
					const unsigned min_repair_time = window->min_fill_time;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&min_repair_time, sizeof(min_repair_time) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKSVCTIMEMEAN:
				{
					const unsigned mean_repair_time = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_SVC_TIME_MEAN];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&mean_repair_time, sizeof(mean_repair_time) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKSVCTIMEMAX:
				{
					const unsigned max_repair_time = window->max_fill_time;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&max_repair_time, sizeof(max_repair_time) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKFAILTIMEMIN:
				{
					const unsigned min_fail_time = peer->min_fail_time;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&min_fail_time, sizeof(min_fail_time) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKFAILTIMEMEAN:
				{
					const unsigned mean_fail_time = peer->cumulative_stats[PGM_PC_RECEIVER_NAK_FAIL_TIME_MEAN];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&mean_fail_time, sizeof(mean_fail_time) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKFAILTIMEMAX:
				{
					const unsigned max_fail_time = peer->max_fail_time;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&max_fail_time, sizeof(max_fail_time) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKTRANSMITMIN:
				{
					const unsigned min_transmit_count = window->min_nak_transmit_count;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&min_transmit_count, sizeof(min_transmit_count) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKTRANSMITMEAN:
				{
					const unsigned mean_transmit_count = peer->cumulative_stats[PGM_PC_RECEIVER_TRANSMIT_MEAN];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&mean_transmit_count, sizeof(mean_transmit_count) );
				}
				break;
		
			case COLUMN_PGMRECEIVERNAKTRANSMITMAX:
				{
					const unsigned max_transmit_count = window->max_nak_transmit_count;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&max_transmit_count, sizeof(max_transmit_count) );
				}
				break;
	
			case COLUMN_PGMRECEIVERACKSSENT:
				{
					const unsigned acks_sent = peer->cumulative_stats[PGM_PC_RECEIVER_ACKS_SENT];
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&acks_sent, sizeof(acks_sent) );
				}
				break;
		
			case COLUMN_PGMRECEIVERRXWTRAIL:
				{
					const unsigned rxw_trail = window->rxw_trail;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&rxw_trail, sizeof(rxw_trail) );
				}
				break;
		
			case COLUMN_PGMRECEIVERRXWLEAD:
				{
					const unsigned rxw_lead = window->lead;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&rxw_lead, sizeof(rxw_lead) );
				}
				break;
	
/* TODO: traps */	
			case COLUMN_PGMRECEIVERNAKFAILURESLASTINTERVAL:
			case COLUMN_PGMRECEIVERLASTINTERVALNAKFAILURES:
				{
					const unsigned failures = 0;
					snmp_set_var_typed_value (var, ASN_COUNTER, /* ASN_COUNTER32 */
								  (const u_char*)&failures, sizeof(failures) );
				}
				break;

			default:
				snmp_log (LOG_ERR, "pgmReceiverTable_handler: unknown column.\n");
				break;
			}
		}
		break;

	case MODE_SET_RESERVE1:
	default:
		snmp_log (LOG_ERR, "pgmReceiverTable_handler: unsupported mode.\n");
		break;

	}

	return SNMP_ERR_NOERROR;
}

/*
 * SNMP TRAPS
 */

PGM_GNUC_INTERNAL
int
send_pgmStart_trap (void)
{
	pgm_debug ("send_pgmStart_trap ()");

	netsnmp_variable_list  *var_list = NULL;
	static const oid pgmStart_oid[] = { 1,3,6,1,3,112,2,0,1 };

/*
 * Set the snmpTrapOid.0 value
 */
	snmp_varlist_add_variable (&var_list,
				   snmptrap_oid, OID_LENGTH( snmptrap_oid ),
				   ASN_OBJECT_ID,
				   (const u_char*)pgmStart_oid, sizeof(pgmStart_oid));
/*
 * Add any extra (optional) objects here
 */

/*
 * Send the trap to the list of configured destinations
 *  and clean up
 */
	send_v2trap (var_list);
	snmp_free_varbind (var_list);
	return SNMP_ERR_NOERROR;
}

PGM_GNUC_INTERNAL
int
send_pgmStop_trap (void)
{
	pgm_debug ("send_pgmStop_trap ()");

	netsnmp_variable_list  *var_list = NULL;
	static const oid pgmStop_oid[] = { 1,3,6,1,3,112,2,0,2 };

/*
 * Set the snmpTrapOid.0 value
 */
	snmp_varlist_add_variable (&var_list,
				   snmptrap_oid, OID_LENGTH(snmptrap_oid),
				   ASN_OBJECT_ID,
				   (const u_char*)pgmStop_oid, sizeof(pgmStop_oid));
    

/*
 * Add any extra (optional) objects here
 */

/*
 * Send the trap to the list of configured destinations
 *  and clean up
 */
	send_v2trap (var_list);
	snmp_free_varbind (var_list);
	return SNMP_ERR_NOERROR;
}

PGM_GNUC_INTERNAL
int
send_pgmNewSourceTrap_trap (void)
{
	pgm_debug ("send_pgmNewSourceTrap_trap ()");

	netsnmp_variable_list  *var_list = NULL;
	static const oid pgmNewSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,3 };
	static const oid pgmSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,6, /* insert index here */ };
	static const oid pgmSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,7, /* insert index here */ };

/*
 * Set the snmpTrapOid.0 value
 */
	snmp_varlist_add_variable (&var_list,
				   snmptrap_oid, OID_LENGTH(snmptrap_oid),
				   ASN_OBJECT_ID,
				   (const u_char*)pgmNewSourceTrap_oid, sizeof(pgmNewSourceTrap_oid));
/*
 * Add any objects from the trap definition
 */
	snmp_varlist_add_variable (&var_list,
				   pgmSourceSourceGsi_oid, OID_LENGTH(pgmSourceSourceGsi_oid),
				   ASN_OCTET_STR,
/* Set an appropriate value for pgmSourceSourceGsi */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmSourceSourcePortNumber_oid, OID_LENGTH(pgmSourceSourcePortNumber_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmSourceSourcePortNumber */
				   NULL, 0);
/*
 * Add any extra (optional) objects here
 */

/*
 * Send the trap to the list of configured destinations
 *  and clean up
 */
	send_v2trap (var_list);
	snmp_free_varbind (var_list);
	return SNMP_ERR_NOERROR;
}

PGM_GNUC_INTERNAL
int
send_pgmClosedSourceTrap_trap (void)
{
	pgm_debug ("send_pgmClosedSourceTrap_trap ()");

	netsnmp_variable_list  *var_list = NULL;
	static const oid pgmClosedSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,4 };
	static const oid pgmSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,6, /* insert index here */ };
	static const oid pgmSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,2,100,2,1,7, /* insert index here */ };

/*
 * Set the snmpTrapOid.0 value
 */
	snmp_varlist_add_variable (&var_list,
				   snmptrap_oid, OID_LENGTH(snmptrap_oid),
				   ASN_OBJECT_ID,
				   (const u_char*)pgmClosedSourceTrap_oid, sizeof(pgmClosedSourceTrap_oid));
/*
 * Add any objects from the trap definition
 */
	snmp_varlist_add_variable (&var_list,
				   pgmSourceSourceGsi_oid, OID_LENGTH(pgmSourceSourceGsi_oid),
				   ASN_OCTET_STR,
/* Set an appropriate value for pgmSourceSourceGsi */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmSourceSourcePortNumber_oid, OID_LENGTH(pgmSourceSourcePortNumber_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmSourceSourcePortNumber */
				   NULL, 0);
/*
 * Add any extra (optional) objects here
 */

/*
 * Send the trap to the list of configured destinations
 *  and clean up
 */
	send_v2trap (var_list);
	snmp_free_varbind (var_list);
	return SNMP_ERR_NOERROR;
}

PGM_GNUC_INTERNAL
int
send_pgmNewReceiverTrap_trap (void)
{
	pgm_debug ("send_pgmNewReceiverTrap_trap ()");

	netsnmp_variable_list  *var_list = NULL;
	static const oid pgmNewReceiverTrap_oid[] = { 1,3,6,1,3,112,2,0,5 };
	static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ };
	static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ };
	static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ };

/*
 * Set the snmpTrapOid.0 value
 */
	snmp_varlist_add_variable (&var_list,
				   snmptrap_oid, OID_LENGTH(snmptrap_oid),
				   ASN_OBJECT_ID,
				   (const u_char*)pgmNewReceiverTrap_oid, sizeof(pgmNewReceiverTrap_oid));
/*
 * Add any objects from the trap definition
 */
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid),
				   ASN_OCTET_STR,
/* Set an appropriate value for pgmReceiverSourceGsi */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmReceiverSourcePortNumber */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmReceiverUniqueInstance */
				   NULL, 0);
/*
 * Add any extra (optional) objects here
 */

/*
 * Send the trap to the list of configured destinations
 *  and clean up
 */
	send_v2trap (var_list);
	snmp_free_varbind (var_list);
	return SNMP_ERR_NOERROR;
}

PGM_GNUC_INTERNAL
int
send_pgmClosedReceiverTrap_trap (void)
{
	pgm_debug ("send_pgmClosedReceiverTrap_trap ()");

	netsnmp_variable_list  *var_list = NULL;
	static const oid pgmClosedReceiverTrap_oid[] = { 1,3,6,1,3,112,2,0,6 };
	static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ };
	static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ };
	static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ };

/*
 * Set the snmpTrapOid.0 value
 */
	snmp_varlist_add_variable (&var_list,
				   snmptrap_oid, OID_LENGTH(snmptrap_oid),
				   ASN_OBJECT_ID,
				   (const u_char*)pgmClosedReceiverTrap_oid, sizeof(pgmClosedReceiverTrap_oid));
/*
 * Add any objects from the trap definition
 */
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid),
				   ASN_OCTET_STR,
/* Set an appropriate value for pgmReceiverSourceGsi */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmReceiverSourcePortNumber */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmReceiverUniqueInstance */
				   NULL, 0);
/*
 * Add any extra (optional) objects here
 */

/*
 * Send the trap to the list of configured destinations
 *  and clean up
 */
	send_v2trap (var_list);
	snmp_free_varbind (var_list);
	return SNMP_ERR_NOERROR;
}

PGM_GNUC_INTERNAL
int
send_pgmNakFailuresTrap_trap (void)
{
	pgm_debug ("send_pgmNakFailuresTrap_trap ()");

	netsnmp_variable_list  *var_list = NULL;
	static const oid pgmNakFailuresTrap_oid[] = { 1,3,6,1,3,112,2,0,7 };
	static const oid pgmReceiverSourceGsi_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,8, /* insert index here */ };
	static const oid pgmReceiverSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,9, /* insert index here */ };
	static const oid pgmReceiverUniqueInstance_oid[] = { 1,3,6,1,3,112,1,3,100,2,1,10, /* insert index here */ };
	static const oid pgmReceiverNakFailureThresholdTimer_oid[] = { 1,3,6,1,3,112,1,3,100,3,1,14, /* insert index here */ };
	static const oid pgmReceiverNakFailureThreshold_oid[] = { 1,3,6,1,3,112,1,3,100,3,1,15, /* insert index here */ };
	static const oid pgmReceiverNakFailuresLastInterval_oid[] = { 1,3,6,1,3,112,1,3,100,4,1,56, /* insert index here */ };
	static const oid pgmReceiverLastIntervalNakFailures_oid[] = { 1,3,6,1,3,112,1,3,100,4,1,57, /* insert index here */ };

/*
 * Set the snmpTrapOid.0 value
 */
	snmp_varlist_add_variable (&var_list,
				   snmptrap_oid, OID_LENGTH(snmptrap_oid),
				   ASN_OBJECT_ID,
				   (const u_char*)pgmNakFailuresTrap_oid, sizeof(pgmNakFailuresTrap_oid));
/*
 * Add any objects from the trap definition
 */
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverSourceGsi_oid, OID_LENGTH(pgmReceiverSourceGsi_oid),
				   ASN_OCTET_STR,
/* Set an appropriate value for pgmReceiverSourceGsi */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverSourcePortNumber_oid, OID_LENGTH(pgmReceiverSourcePortNumber_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmReceiverSourcePortNumber */
        NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverUniqueInstance_oid, OID_LENGTH(pgmReceiverUniqueInstance_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmReceiverUniqueInstance */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverNakFailureThresholdTimer_oid, OID_LENGTH(pgmReceiverNakFailureThresholdTimer_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmReceiverNakFailureThresholdTimer */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverNakFailureThreshold_oid, OID_LENGTH(pgmReceiverNakFailureThreshold_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmReceiverNakFailureThreshold */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverNakFailuresLastInterval_oid, OID_LENGTH(pgmReceiverNakFailuresLastInterval_oid),
				   ASN_COUNTER,
/* Set an appropriate value for pgmReceiverNakFailuresLastInterval */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmReceiverLastIntervalNakFailures_oid, OID_LENGTH(pgmReceiverLastIntervalNakFailures_oid),
				   ASN_COUNTER,
/* Set an appropriate value for pgmReceiverLastIntervalNakFailures */
				   NULL, 0);
/*
 * Add any extra (optional) objects here
 */

/*
 * Send the trap to the list of configured destinations
 *  and clean up
 */
	send_v2trap (var_list);
	snmp_free_varbind (var_list);
	return SNMP_ERR_NOERROR;
}

PGM_GNUC_INTERNAL
int
send_pgmNewDlrSourceTrap_trap (void)
{
	pgm_debug ("send_pgmNewDlrSourceTrap_trap ()");

	netsnmp_variable_list  *var_list = NULL;
	static const oid pgmNewDlrSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,8 };
	static const oid pgmDlrSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,4, /* insert index here */ };
	static const oid pgmDlrSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,5, /* insert index here */ };

/*
 * Set the snmpTrapOid.0 value
 */
	snmp_varlist_add_variable (&var_list,
				   snmptrap_oid, OID_LENGTH(snmptrap_oid),
				   ASN_OBJECT_ID,
				   (const u_char*)pgmNewDlrSourceTrap_oid, sizeof(pgmNewDlrSourceTrap_oid));
/*
 * Add any objects from the trap definition
 */
	snmp_varlist_add_variable (&var_list,
				   pgmDlrSourceSourceGsi_oid, OID_LENGTH(pgmDlrSourceSourceGsi_oid),
				   ASN_OCTET_STR,
/* Set an appropriate value for pgmDlrSourceSourceGsi */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmDlrSourceSourcePortNumber_oid, OID_LENGTH(pgmDlrSourceSourcePortNumber_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmDlrSourceSourcePortNumber */
				   NULL, 0);
/*
 * Add any extra (optional) objects here
 */

/*
 * Send the trap to the list of configured destinations
 *  and clean up
 */
	send_v2trap (var_list);
	snmp_free_varbind (var_list);
	return SNMP_ERR_NOERROR;
}

PGM_GNUC_INTERNAL
int
send_pgmClosedDlrSourceTrap_trap (void)
{
	pgm_debug ("send_pgmClosedDlrSourceTrap_trap ()");

	netsnmp_variable_list  *var_list = NULL;
	static const oid pgmClosedDlrSourceTrap_oid[] = { 1,3,6,1,3,112,2,0,9 };
	static const oid pgmDlrSourceSourceGsi_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,4, /* insert index here */ };
	static const oid pgmDlrSourceSourcePortNumber_oid[] = { 1,3,6,1,3,112,1,4,100,2,1,5, /* insert index here */ };

/*
 * Set the snmpTrapOid.0 value
 */
	snmp_varlist_add_variable (&var_list,
				   snmptrap_oid, OID_LENGTH(snmptrap_oid),
				   ASN_OBJECT_ID,
				   (const u_char*)pgmClosedDlrSourceTrap_oid, sizeof(pgmClosedDlrSourceTrap_oid));
    
/*
 * Add any objects from the trap definition
 */
	snmp_varlist_add_variable (&var_list,
				   pgmDlrSourceSourceGsi_oid, OID_LENGTH(pgmDlrSourceSourceGsi_oid),
				   ASN_OCTET_STR,
/* Set an appropriate value for pgmDlrSourceSourceGsi */
				   NULL, 0);
	snmp_varlist_add_variable (&var_list,
				   pgmDlrSourceSourcePortNumber_oid, OID_LENGTH(pgmDlrSourceSourcePortNumber_oid),
				   ASN_UNSIGNED,
/* Set an appropriate value for pgmDlrSourceSourcePortNumber */
				   NULL, 0);
/*
 * Add any extra (optional) objects here
 */

/*
 * Send the trap to the list of configured destinations
 *  and clean up
 */
	send_v2trap (var_list);
	snmp_free_varbind (var_list);
	return SNMP_ERR_NOERROR;
}

/* eof */
