Clixon SNMP frontend update

Integrated netsnmp agent and clixon event handling, made a workaround of fdset:s
used in the netsnmp API with sockets used in Clixon
Added a hardcoded MIB OID handler as subagent
Added libnetsnmpagent to autotools dependency check
New OE_SNMP error code
This commit is contained in:
Olof hagsand 2022-04-25 10:59:18 +02:00
parent 16d21bbe3b
commit e32cb81b49
5 changed files with 244 additions and 46 deletions

View file

@ -30,7 +30,6 @@
the terms of any one of the Apache License version 2 or the GPL. the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -45,7 +44,10 @@
#include <sys/types.h> #include <sys/types.h>
#include <signal.h> #include <signal.h>
/* net-snmp */
#include <net-snmp/net-snmp-config.h> #include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
/* cligen */ /* cligen */
#include <cligen/cligen.h> #include <cligen/cligen.h>
@ -56,18 +58,181 @@
/* Command line options to be passed to getopt(3) */ /* Command line options to be passed to getopt(3) */
#define SNMP_OPTS "hD:f:l:o:" #define SNMP_OPTS "hD:f:l:o:"
/*! XXX netsnmp API goes here #if 1 // XXX hardcoded MIB object from https://github.com/net-snmp/subagent-example/blob/master/example-demon.c
/* cp NET-SNMP-TUTORIAL-MIB.txt ~/.snmp/mibs/
* sudo /usr/local/sbin/snmpd -Lo -C --rwcommunity=public --master=agentx -f
* sudo example_demon
* snmpget -v 2c -c public localhost NET-SNMP-TUTORIAL-MIB::nstAgentSubagentObject.0
*/
/*!
* our initialization routine, automatically called by the agent
* (to get called, the function name must match init_FILENAME())
* the variable we want to tie an OID to. The agent will handle all
* * GET and SET requests to this variable changing it's value as needed.
*/
static long nstAgentSubagentObject = 2;
static void
init_nstAgentSubagentObject(clicon_handle h)
{
static oid nstAgentSubagentObject_oid[] =
{ 1, 3, 6, 1, 4, 1, 8072, 2, 4, 1, 1, 2, 0 };
clicon_debug(1, "%s", __FUNCTION__);
/*
* a debugging statement. Run the agent with -DnstAgentSubagentObject to see
* the output of this debugging statement.
*/
DEBUGMSGTL(("nstAgentSubagentObject",
"Initializing the nstAgentSubagentObject module\n"));
/*
* the line below registers our variables defined above as
* accessible and makes it writable. A read only version of any
* of these registration would merely call
* register_read_only_long_instance() instead. The functions
* called below should be consistent with your MIB, however.
*
* If we wanted a callback when the value was retrieved or set
* (even though the details of doing this are handled for you),
* you could change the NULL pointer below to a valid handler
* function.
*/
DEBUGMSGTL(("nstAgentSubagentObject",
"Initalizing nstAgentSubagentObject scalar integer. Default value = %ld\n",
nstAgentSubagentObject));
netsnmp_register_long_instance("nstAgentSubagentObject",
nstAgentSubagentObject_oid,
OID_LENGTH(nstAgentSubagentObject_oid),
&nstAgentSubagentObject, NULL);
DEBUGMSGTL(("nstAgentSubagentObject",
"Done initalizing nstAgentSubagentObject module\n"));
}
#endif // XXX Hardcoded
/*! Signal terminates process
* Just set exit flag for proper exit in event loop
*/
static void
clixon_snmp_sig_term(int arg)
{
clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d",
__PROGRAM__, __FUNCTION__, getpid(), arg);
/* This should ensure no more accepts or incoming packets are processed because next time eventloop
* is entered, it will terminate.
* However there may be a case of sockets closing rather abruptly for clients
*/
clixon_exit_set(1);
}
/*! Callback for single socket
* This is a workaround for netsnmps API usiing fdset:s, instead an fdset is created before calling
* the snmp api
* @param[in] s Read socket
* @param[in] arg Clixon handle
*/ */
static int static int
snmp_input_cb(int s, clixon_snmp_input_cb(int s,
void *arg) void *arg)
{ {
int retval = -1; int retval = -1;
fd_set readfds;
// clicon_handle h = (clicon_handle)arg;
clicon_debug(1, "%s", __FUNCTION__);
FD_ZERO(&readfds);
FD_SET(s, &readfds);
snmp_read(&readfds);
retval = 0; retval = 0;
// done: // done:
return retval; return retval;
} }
/*! Get which sockets are used from SNMP API, the register single sockets into clixon event system
*
* This is a workaround for netsnmps API usiing fdset:s, instead an fdset is created before calling
* the snmp api
* if you use select(), see snmp_select_info() in snmp_api(3)
* snmp_select_info(int *numfds, fd_set *fdset, struct timeval *timeout, int *block)
* @see clixon_snmp_input_cb
*/
static int
clixon_snmp_fdset_register(clicon_handle h)
{
int retval = -1;
int numfds = 0;
fd_set readfds;
struct timeval timeout = { LONG_MAX, 0 };
int block = 0;
int nr;
int i;
FD_ZERO(&readfds);
if ((nr = snmp_sess_select_info(NULL, &numfds, &readfds, &timeout, &block)) < 0){
clicon_err(OE_SNMP, errno, "snmp_select_error");
goto done;
}
for (i=0; i<numfds; i++){
if (FD_ISSET(i, &readfds)){
if (clixon_event_reg_fd(i, clixon_snmp_input_cb, h, "snmp socket") < 0)
goto done;
}
}
retval = 0;
done:
return retval;
}
/*! Init netsnmp agent connection
* @param[in] h Clixon handle
* @param[in] logdst Log destination, see clixon_log.h
* @see snmp_terminate
*/
static int
clixon_snmp_init(clicon_handle h,
int logdst)
{
int retval = -1;
clicon_debug(1, "%s", __FUNCTION__);
if (logdst == CLICON_LOG_SYSLOG)
snmp_enable_calllog();
else
snmp_enable_stderrlog();
/* make a agentx client. */
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);
/* initialize the agent library */
init_agent(__PROGRAM__);
/* XXX Hardcoded, replace this with generic MIB */
init_nstAgentSubagentObject(h);
/* example-demon will be used to read example-demon.conf files. */
init_snmp(__PROGRAM__);
if (set_signal(SIGTERM, clixon_snmp_sig_term, NULL) < 0){
clicon_err(OE_DAEMON, errno, "Setting signal");
goto done;
}
if (set_signal(SIGINT, clixon_snmp_sig_term, NULL) < 0){
clicon_err(OE_DAEMON, errno, "Setting signal");
goto done;
}
if (set_signal(SIGPIPE, SIG_IGN, NULL) < 0){
clicon_err(OE_UNIX, errno, "Setting DIGPIPE signal");
goto done;
}
/* Workaround for netsnmps API use of fdset:s instead of sockets */
if (clixon_snmp_fdset_register(h) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Clean and close all state of netconf process (but dont exit). /*! Clean and close all state of netconf process (but dont exit).
* Cannot use h after this * Cannot use h after this
@ -80,6 +245,7 @@ snmp_terminate(clicon_handle h)
cvec *nsctx; cvec *nsctx;
cxobj *x; cxobj *x;
shutdown_agent();
clicon_rpc_close_session(h); clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL) if ((yspec = clicon_dbspec_yang(h)) != NULL)
ys_free(yspec); ys_free(yspec);
@ -97,21 +263,6 @@ snmp_terminate(clicon_handle h)
return 0; return 0;
} }
/*! Setup signal handlers
*/
static int
snmp_signal_init (clicon_handle h)
{
int retval = -1;
if (set_signal(SIGPIPE, SIG_IGN, NULL) < 0){
clicon_err(OE_UNIX, errno, "Setting DIGPIPE signal");
goto done;
}
retval = 0;
done:
return retval;
}
/*! Usage help routine /*! Usage help routine
* @param[in] h Clixon handle * @param[in] h Clixon handle
@ -194,6 +345,7 @@ main(int argc,
*/ */
clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst); clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
clicon_debug_init(dbg, NULL); clicon_debug_init(dbg, NULL);
yang_init(h); yang_init(h);
/* Find, read and parse configfile */ /* Find, read and parse configfile */
@ -246,13 +398,11 @@ main(int argc,
if (netconf_module_features(h) < 0) if (netconf_module_features(h) < 0)
goto done; goto done;
/* Setup signal handlers, int particular PIPE that occurs if backend closes / restarts */
if (snmp_signal_init(h) < 0)
goto done;
/* In case ietf-yang-metadata is loaded by application, handle annotation extension */ /* In case ietf-yang-metadata is loaded by application, handle annotation extension */
if (0 && yang_metadata_init(h) < 0) #if 0
if (yang_metadata_init(h) < 0)
goto done; goto done;
#endif
/* Create top-level yang spec and store as option */ /* Create top-level yang spec and store as option */
if ((yspec = yspec_new()) == NULL) if ((yspec = yspec_new()) == NULL)
goto done; goto done;
@ -292,13 +442,13 @@ main(int argc,
if (clicon_nsctx_global_set(h, nsctx_global) < 0) if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done; goto done;
#if 0
/* Call start function is all plugins before we go interactive */ /* Call start function is all plugins before we go interactive */
if (clixon_plugin_start_all(h) < 0) if (clixon_plugin_start_all(h) < 0)
goto done; goto done;
#if 1
/* XXX get session id from backend hello */
clicon_session_id_set(h, getpid());
#endif #endif
/* Get session id from backend hello */
clicon_session_id_set(h, getpid());
/* Send hello request to backend to get session-id back /* Send hello request to backend to get session-id back
* This is done once at the beginning of the session and then this is * This is done once at the beginning of the session and then this is
@ -309,12 +459,10 @@ main(int argc,
goto done; goto done;
clicon_session_id_set(h, id); clicon_session_id_set(h, id);
/* XXX Here should register an net-snmp interface socket /* Init snmp as subagent */
* But it registers stdin which is wrong if (clixon_snmp_init(h, logdst) < 0)
*/
if (clixon_event_reg_fd(0, snmp_input_cb, h, "snmp socket") < 0)
goto done; goto done;
if (dbg) if (dbg)
clicon_option_dump(h, dbg); clicon_option_dump(h, dbg);
if (clixon_event_loop(h) < 0) if (clixon_event_loop(h) < 0)

67
configure vendored
View file

@ -5324,10 +5324,10 @@ $as_echo "checking netsnmp is enabled: $enable_netsnmp" >&6; }
if test "$enable_netsnmp" = "yes"; then if test "$enable_netsnmp" = "yes"; then
# All libs are: # All libs are:
# libnetsnmp, libnetsnmpmibs, libnetsnmpagent, libnetsnmptrapd, libnetsnmphelpers # libnetsnmp, libnetsnmpagent, libnetsnmpmibs, libnetsnmptrapd, libnetsnmphelpers
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for snmp_open in -lnetsnmp" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for init_snmp in -lnetsnmp" >&5
$as_echo_n "checking for snmp_open in -lnetsnmp... " >&6; } $as_echo_n "checking for init_snmp in -lnetsnmp... " >&6; }
if ${ac_cv_lib_netsnmp_snmp_open+:} false; then : if ${ac_cv_lib_netsnmp_init_snmp+:} false; then :
$as_echo_n "(cached) " >&6 $as_echo_n "(cached) " >&6
else else
ac_check_lib_save_LIBS=$LIBS ac_check_lib_save_LIBS=$LIBS
@ -5341,33 +5341,78 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
#endif #endif
char snmp_open (); char init_snmp ();
int int
main () main ()
{ {
return snmp_open (); return init_snmp ();
; ;
return 0; return 0;
} }
_ACEOF _ACEOF
if ac_fn_c_try_link "$LINENO"; then : if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_netsnmp_snmp_open=yes ac_cv_lib_netsnmp_init_snmp=yes
else else
ac_cv_lib_netsnmp_snmp_open=no ac_cv_lib_netsnmp_init_snmp=no
fi fi
rm -f core conftest.err conftest.$ac_objext \ rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS LIBS=$ac_check_lib_save_LIBS
fi fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_netsnmp_snmp_open" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_netsnmp_init_snmp" >&5
$as_echo "$ac_cv_lib_netsnmp_snmp_open" >&6; } $as_echo "$ac_cv_lib_netsnmp_init_snmp" >&6; }
if test "x$ac_cv_lib_netsnmp_snmp_open" = xyes; then : if test "x$ac_cv_lib_netsnmp_init_snmp" = xyes; then :
cat >>confdefs.h <<_ACEOF cat >>confdefs.h <<_ACEOF
#define HAVE_LIBNETSNMP 1 #define HAVE_LIBNETSNMP 1
_ACEOF _ACEOF
LIBS="-lnetsnmp $LIBS" LIBS="-lnetsnmp $LIBS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for init_agent in -lnetsnmpagent" >&5
$as_echo_n "checking for init_agent in -lnetsnmpagent... " >&6; }
if ${ac_cv_lib_netsnmpagent_init_agent+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lnetsnmpagent $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char init_agent ();
int
main ()
{
return init_agent ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_netsnmpagent_init_agent=yes
else
ac_cv_lib_netsnmpagent_init_agent=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_netsnmpagent_init_agent" >&5
$as_echo "$ac_cv_lib_netsnmpagent_init_agent" >&6; }
if test "x$ac_cv_lib_netsnmpagent_init_agent" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBNETSNMPAGENT 1
_ACEOF
LIBS="-lnetsnmpagent $LIBS"
fi fi
for ac_header in net-snmp/net-snmp-config.h for ac_header in net-snmp/net-snmp-config.h

View file

@ -289,8 +289,9 @@ AC_MSG_RESULT(checking netsnmp is enabled: $enable_netsnmp)
if test "$enable_netsnmp" = "yes"; then if test "$enable_netsnmp" = "yes"; then
# All libs are: # All libs are:
# libnetsnmp, libnetsnmpmibs, libnetsnmpagent, libnetsnmptrapd, libnetsnmphelpers # libnetsnmp, libnetsnmpagent, libnetsnmpmibs, libnetsnmptrapd, libnetsnmphelpers
AC_CHECK_LIB(netsnmp, snmp_open) AC_CHECK_LIB(netsnmp, init_snmp)
AC_CHECK_LIB(netsnmpagent, init_agent)
AC_CHECK_HEADERS(net-snmp/net-snmp-config.h,[], AC_MSG_ERROR([snmp is missing])) AC_CHECK_HEADERS(net-snmp/net-snmp-config.h,[], AC_MSG_ERROR([snmp is missing]))
fi fi

View file

@ -63,6 +63,9 @@
/* Define to 1 if you have the `netsnmp' library (-lnetsnmp). */ /* Define to 1 if you have the `netsnmp' library (-lnetsnmp). */
#undef HAVE_LIBNETSNMP #undef HAVE_LIBNETSNMP
/* Define to 1 if you have the `netsnmpagent' library (-lnetsnmpagent). */
#undef HAVE_LIBNETSNMPAGENT
/* Define to 1 if you have the `nghttp2' library (-lnghttp2). */ /* Define to 1 if you have the `nghttp2' library (-lnghttp2). */
#undef HAVE_LIBNGHTTP2 #undef HAVE_LIBNGHTTP2

View file

@ -74,6 +74,7 @@ enum clicon_err{
OE_RESTCONF, /* RESTCONF errors */ OE_RESTCONF, /* RESTCONF errors */
OE_PLUGIN, /* plugin loading, etc */ OE_PLUGIN, /* plugin loading, etc */
OE_YANG , /* Yang error */ OE_YANG , /* Yang error */
OE_SNMP , /* Netsnmp error */
OE_FATAL, /* Fatal error */ OE_FATAL, /* Fatal error */
OE_UNDEF, OE_UNDEF,
/*-- From here error extensions using clixon_err_cat_reg, XXX register dynamically? --*/ /*-- From here error extensions using clixon_err_cat_reg, XXX register dynamically? --*/