Merge branch 'snmp'

This commit is contained in:
Olof hagsand 2022-07-15 15:54:44 +02:00
commit 1d78241115
47 changed files with 8494 additions and 46 deletions

View file

@ -36,6 +36,7 @@ VPATH = @srcdir@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
with_restconf = @with_restconf@
enable_netsnmp = @enable_netsnmp@
SHELL = /bin/sh
@ -47,6 +48,11 @@ SUBDIRS += netconf
ifdef with_restconf
SUBDIRS += restconf
endif
ifdef enable_netsnmp
ifeq ($(enable_netsnmp),yes)
SUBDIRS += snmp
endif
endif
.PHONY: all clean depend install $(SUBDIRS)

View file

@ -453,7 +453,7 @@ usage(clicon_handle h,
"\t-p <dir>\tAdd Yang directory path (see CLICON_YANG_DIR)\n"
"\t-b <dir>\tSpecify datastore directory\n"
"\t-F\t\tRun in foreground, do not run as daemon\n"
"\t-z\t\tKill other config daemon and exit\n"
"\t-z\t\tKill other backend daemon and exit\n"
"\t-a UNIX|IPv4|IPv6 Internal backend socket family\n"
"\t-u <path|addr>\tInternal socket domain path or IP addr (see -a)(default: %s)\n"
"\t-P <file>\tPid filename (default: %s)\n"
@ -1004,7 +1004,7 @@ main(int argc,
goto done;
/* Write pid-file */
if ((pid = pidfile_write(pidfile)) < 0)
if (pidfile_write(pidfile) < 0)
goto done;
if (set_signal(SIGTERM, backend_sig_term, NULL) < 0){

View file

@ -861,7 +861,7 @@ cli_show_options(clicon_handle h,
else
fprintf(stdout, "%s: NULL\n", keys[i]);
}
/* Next print CLICON_FEATURE and CLICON_YANG_DIR from config tree
/* Next print CLICON_FEATURE, CLICON_YANG_DIR and CLICON_SNMP_MIB from config tree
* Since they are lists they are placed in the config tree.
*/
x = NULL;
@ -876,6 +876,12 @@ cli_show_options(clicon_handle h,
continue;
fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x));
}
x = NULL;
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x), "CLICON_SNMP_MIB") != 0)
continue;
fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x));
}
retval = 0;
done:
if (keys)

134
apps/snmp/Makefile.in Normal file
View file

@ -0,0 +1,134 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
#
# This file is part of CLIXON
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Alternatively, the contents of this file may be used under the terms of
# the GNU General Public License Version 3 or later (the "GPL"),
# in which case the provisions of the GPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of the GPL, and not to allow others to
# use your version of this file under the terms of Apache License version 2,
# indicate your decision by deleting the provisions above and replace them with
# the notice and other provisions required by the GPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the Apache License version 2 or the GPL.
#
# ***** END LICENSE BLOCK *****
#
#
VPATH = @srcdir@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
CC = @CC@
CFLAGS = @CFLAGS@
LINKAGE = @LINKAGE@
INSTALLFLAGS = @INSTALLFLAGS@
LDFLAGS = @LDFLAGS@
prefix = @prefix@
datarootdir = @datarootdir@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
sbindir = @sbindir@
mandir = @mandir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
sysconfdir = @sysconfdir@
includedir = @includedir@
HOST_VENDOR = @host_vendor@
SH_SUFFIX = @SH_SUFFIX@
LIBSTATIC_SUFFIX = @LIBSTATIC_SUFFIX@
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
CLIXON_MINOR = @CLIXON_VERSION_MINOR@
# Use this clixon lib for linking
ifeq ($(LINKAGE),dynamic)
CLIXON_LIB = libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR)
else
CLIXON_LIB = libclixon$(LIBSTATIC_SUFFIX)
endif
# For dependency
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src $(top_srcdir)/lib/src/$(CLIXON_LIB) @LIBS@
CPPFLAGS = @CPPFLAGS@
ifeq ($(LINKAGE),dynamic)
CPPFLAGS += -fPIC
endif
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
# Application
APPL = clixon_snmp
# Common source - not accessible from plugin - independent of restconf package (fcgi|native)
APPSRC =
APPSRC += snmp_main.c
APPSRC += snmp_register.c
APPSRC += snmp_handler.c
APPSRC += snmp_lib.c
APPOBJ = $(APPSRC:.c=.o)
all: $(APPL)
# Dependency of clixon library (LIBDEPS)
$(top_srcdir)/lib/src/$(CLIXON_LIB):
(cd $(top_srcdir)/lib/src && $(MAKE) $(MFLAGS) $(CLIXON_LIB))
clean:
rm -f *.core $(APPL) $(APPOBJ) *.o
rm -f *.gcda *.gcno *.gcov # coverage
distclean: clean
rm -f Makefile *~ .depend
# Put daemon in bin
# Put other executables in libexec/
install: $(APPL)
install -d -m 0755 $(DESTDIR)$(sbindir)
install -m 0755 $(INSTALLFLAGS) $(APPL) $(DESTDIR)$(sbindir)
install-include:
uninstall:
rm -f $(DESTDIR)$(sbindir)/$(APPL)
.SUFFIXES:
.SUFFIXES: .c .o
.c.o:
$(CC) $(INCLUDES) -D__PROGRAM__=\"clixon_snmp\" $(CPPFLAGS) $(CFLAGS) -c $<
$(APPL) : $(APPOBJ) $(LIBDEPS)
echo $(APPOBJ)
echo $(LIBDEPS)
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
TAGS:
find . -name '*.[chyl]' -print | etags -
depend:
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(APPFCGI) $(APPSRC) > .depend
#include .depend

1360
apps/snmp/snmp_handler.c Normal file

File diff suppressed because it is too large Load diff

61
apps/snmp/snmp_handler.h Normal file
View file

@ -0,0 +1,61 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
Sponsored by Siklu Communications LTD
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _SNMP_HANDLER_H_
#define _SNMP_HANDLER_H_
/*
* Prototypes
*/
int clixon_snmp_table_handler(netsnmp_mib_handler *handler,
netsnmp_handler_registration *nhreg,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests);
int clixon_snmp_scalar_handler(netsnmp_mib_handler *handler,
netsnmp_handler_registration *nhreg,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests);
#endif /* _SNMP_HANDLER_H_ */
#ifdef __cplusplus
} /* extern "C" */
#endif

1208
apps/snmp/snmp_lib.c Normal file

File diff suppressed because it is too large Load diff

117
apps/snmp/snmp_lib.h Normal file
View file

@ -0,0 +1,117 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
Sponsored by Siklu Communications LTD
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _SNMP_LIB_H_
#define _SNMP_LIB_H_
/*
* Constants
*/
/* Need some way to multiplex SNMP_ and MIB errors on OE_SNMP error handler */
#define CLIXON_ERR_SNMP_MIB 0x1000
#define IETF_YANG_SMIV2_NS "urn:ietf:params:xml:ns:yang:ietf-yang-smiv2"
/* Special case/extended Clixon ASN1 types
* Set in type_yang2asn1() if extended is true
* Must be back to proper net-snmp ASN_ types in type_snmp2xml and type_xml2snmp
* before calling netsnmp API
*/
#define CLIXON_ASN_EXTRAS 253 /* Special case clixon address >= this */
#define CLIXON_ASN_PHYS_ADDR 253 /* Special case phy-address */
#define CLIXON_ASN_FIXED_STRING 254 /* RFC2578 Sec 7.7: String-valued, fixed-length */
#define CLIXON_ASN_ROWSTATUS 255
/*
* Types
*/
/* Userdata to pass around in netsmp callbacks
*/
struct clixon_snmp_handle {
clicon_handle sh_h;
yang_stmt *sh_ys; /* Leaf for scalar, list for table */
oid sh_oid[MAX_OID_LEN]; /* OID for debug, may be removed? */
size_t sh_oidlen;
char *sh_default; /* MIB default value leaf only */
cvec *sh_cvk_orig; /* Index/Key variable values (original) */
netsnmp_table_registration_info *sh_table_info; /* To mimic table-handler in libnetsnmp code
* save only to free properly */
};
typedef struct clixon_snmp_handle clixon_snmp_handle;
/*
* Prototypes
*/
int oid_eq(const oid * objid0, size_t objid0len, const oid * objid1, size_t objid1len);
int oid_append(const oid *objid0, size_t *objid0len, const oid *objid1, size_t objid1len);
int oid_cbuf(cbuf *cb, const oid *objid, size_t objidlen);
int oid_print(FILE *f, const oid *objid, size_t objidlen);
int snmp_yang_type_get(yang_stmt *ys, yang_stmt **yrefp, char **origtypep, yang_stmt **yrestypep, char **restypep);
int yangext_oid_get(yang_stmt *yn, oid *objid, size_t *objidlen, char **objidstr);
int snmp_access_str2int(char *modes_str);
const char *snmp_msg_int2str(int msg);
void *snmp_handle_clone(void *arg);
void snmp_handle_free(void *arg);
int type_yang2asn1(yang_stmt *ys, int *asn1_type, int extended);
int type_snmp2xml(yang_stmt *ys,
int *asn1type,
netsnmp_variable_list *requestvb,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests,
char **valstr);
int type_xml2snmp_pre(char *xmlstr, yang_stmt *ys, char **snmpstr);
int type_xml2snmp(char *snmpstr, int *asn1type, u_char **snmpval, size_t *snmplen, char **reason);
int snmp_yang2xpath(yang_stmt *ys, cvec *keyvec, char **xpath);
int snmp_str2oid(char *str, yang_stmt *yi, oid *objid, size_t *objidlen);
int snmp_oid2str(oid **oidi, size_t *oidilen, yang_stmt *yi, cg_var *cv);
int clixon_snmp_err_cb(void *handle, int suberr, cbuf *cb);
int snmp_xmlkey2val_oid(cxobj *xrow, cvec *cvk_name, cvec **cvk_orig, oid *objidk, size_t *objidklen);
/*========== libnetsnmp-specific code =============== */
int clixon_snmp_api_agent_check(void);
int clixon_snmp_api_agent_cleanup(void);
int clixon_snmp_api_oid_find(oid *oid1, size_t oidlen);
#endif /* _SNMP_LIB_H_ */
#ifdef __cplusplus
} /* extern "C" */
#endif

550
apps/snmp/snmp_main.c Normal file
View file

@ -0,0 +1,550 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
Sponsored by Siklu Communications LTD
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* This is the clixon_snmp daemon
* It assumes a netsnmp damon is running.
* - If netsnmp does not run, clixon_snmp will not start
* - If netsnmp dies, clixon_snmp will exit
* - If netsnmp is restarted, clixon_snmp should also be restarted
* It is possible to be more resilient, such as setting a timer and trying again, in fact, libnetsnmp
* has some such mechanisms but these are NOT implemented
* @see RFC 6643 Translation of Structure of Management Information Version 2 (SMIv2)
* MIB Modules to YANG Modules
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <syslog.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
/* net-snmp */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
#include "snmp_lib.h"
#include "snmp_register.h"
/* Command line options to be passed to getopt(3) */
#define SNMP_OPTS "hD:f:l:o:z"
/* Forward */
static int clixon_snmp_input_cb(int s, void *arg);
/*! Return (hardcoded) pid file
*/
static char*
clicon_snmp_pidfile(clicon_handle h)
{
return "/var/tmp/clixon_snmp.pid";
}
/*! 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);
}
/*! Clean and close all state of netconf process (but dont exit).
* Cannot use h after this
* @param[in] h Clixon handle
*/
static int
snmp_terminate(clicon_handle h)
{
yang_stmt *yspec;
cvec *nsctx;
cxobj *x = NULL;
char *pidfile = clicon_snmp_pidfile(h);
snmp_shutdown(__FUNCTION__);
shutdown_agent();
clixon_snmp_api_agent_cleanup();
if (clicon_ptr_get(h, "snmp-rowstatus-tree", (void**)&x) == 0 && x){
xml_free(x);
x = NULL;
}
clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
ys_free(yspec);
if ((yspec = clicon_config_yang(h)) != NULL)
ys_free(yspec);
if ((nsctx = clicon_nsctx_global_get(h)) != NULL)
cvec_free(nsctx);
if ((x = clicon_conf_xml(h)) != NULL)
xml_free(x);
xpath_optimize_exit();
clixon_event_exit();
clicon_handle_exit(h);
clixon_err_exit();
clicon_log_exit();
if (pidfile)
unlink(pidfile);
return 0;
}
/*! Get which sockets are used from SNMP API, the register single sockets into clixon event system
*
* @param[in] h Clixon handle
* @param[in] register if 1 register snmp sockets with event handler. If 0 close and unregister
* 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 regfd)
{
int retval = -1;
int numfds = 0;
fd_set readfds;
struct timeval timeout = { LONG_MAX, 0 };
int block = 0;
int nr;
int s;
FD_ZERO(&readfds);
if ((nr = snmp_sess_select_info(NULL, &numfds, &readfds, &timeout, &block)) < 0){
clicon_err(OE_XML, errno, "snmp_select_error");
goto done;
}
/* eg 4, 6, 8 */
for (s=0; s<numfds; s++){
if (FD_ISSET(s, &readfds)){
clicon_debug(1, "%s %d", __FUNCTION__, s);
if (regfd){
if (clixon_event_reg_fd(s, clixon_snmp_input_cb, h, "snmp socket") < 0)
goto done;
}
else{
if (clixon_event_unreg_fd(s, clixon_snmp_input_cb) < 0)
goto done;
close(s);
}
}
}
retval = 0;
done:
return retval;
}
/*! 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
clixon_snmp_input_cb(int s,
void *arg)
{
int retval = -1;
fd_set readfds;
clicon_handle h = (clicon_handle)arg;
int ret;
clicon_debug(1, "%s %d", __FUNCTION__, s);
FD_ZERO(&readfds);
FD_SET(s, &readfds);
(void)snmp_read(&readfds);
if (clixon_event_poll(s) < 0){
if (errno == EBADF){
clicon_err_reset();
/* Close the active socket */
if (clixon_event_unreg_fd(s, clixon_snmp_input_cb) < 0)
goto done;
close(s);
/* and then the others */
if (clixon_snmp_fdset_register(h, 0) < 0)
goto done;
if ((ret = snmp_close_sessions()) != 1){
clicon_err(OE_SNMP, ret, "snmp_close_sessions");
goto done;
}
/* Signal normal exit to upper layers (=event handling)
* One can signal error and return -1, but it is nicer with an orderly exit
*/
clixon_exit_set(1);
}
else {
clicon_err(OE_UNIX, errno, "poll");
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_subagent(clicon_handle h,
int logdst)
{
int retval = -1;
char *sockpath = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if (logdst == CLICON_LOG_SYSLOG)
snmp_enable_calllog();
else
snmp_enable_stderrlog();
/* 0 if master, 1 if client */
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);
/* don't load config and don't load/save persistent file */
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, 1);
/* don't load persistent file */
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD, 1);
/* don't save persistent file */
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE, 1);
if (clicon_debug_get())
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_VERBOSE, 1);
if ((sockpath = clicon_option_str(h, "CLICON_SNMP_AGENT_SOCK")) == NULL){
clicon_err(OE_XML, 0, "CLICON_SNMP_AGENT_SOCK not set");
goto done;
}
/* XXX: This should be configurable. */
netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, sockpath);
/* initialize the agent library */
init_agent(__PROGRAM__);
/* example-demon will be used to read example-demon.conf files. */
init_snmp(__PROGRAM__);
if (!clixon_snmp_api_agent_check()){
clicon_err(OE_DAEMON, 0, "Connection to SNMP agent failed");
goto done;
}
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 SIGPIPE signal");
goto done;
}
/* Workaround for netsnmps API use of fdset:s instead of sockets */
if (clixon_snmp_fdset_register(h, 1) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Usage help routine
* @param[in] h Clixon handle
* @param[in] argv0 command line
*/
static void
usage(clicon_handle h,
char *argv0)
{
fprintf(stderr, "usage:%s\n"
"where options are\n"
"\t-h\t\tHelp\n"
"\t-D <level>\tDebug level\n"
"\t-f <file>\tConfiguration file (mandatory)\n"
"\t-l (e|o|s|f<file>) Log on std(e)rr, std(o)ut, (s)yslog(default), (f)ile\n"
"\t-z\t\tKill other %s daemon and exit\n"
"\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0, argv0
);
exit(0);
}
int
main(int argc,
char **argv)
{
int retval = -1;
int c;
char *argv0 = argv[0];
clicon_handle h;
int logdst = CLICON_LOG_STDERR;
struct passwd *pw;
yang_stmt *yspec = NULL;
char *str;
uint32_t id;
cvec *nsctx_global = NULL; /* Global namespace context */
size_t cligen_buflen;
size_t cligen_bufthreshold;
int dbg = 0;
size_t sz;
int pid;
char *pidfile = NULL;
struct stat st;
int zap = 0;
/* Create handle */
if ((h = clicon_handle_init()) == NULL)
return -1;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
/* Set username to clixon handle. Use in all communication to backend */
if ((pw = getpwuid(getuid())) == NULL){
clicon_err(OE_UNIX, errno, "getpwuid");
goto done;
}
if (clicon_username_set(h, pw->pw_name) < 0)
goto done;
while ((c = getopt(argc, argv, SNMP_OPTS)) != -1)
switch (c) {
case 'h' : /* help */
usage(h, argv[0]);
break;
case 'D' : /* debug */
if (sscanf(optarg, "%d", &dbg) != 1)
usage(h, argv[0]);
break;
case 'f': /* override config file */
if (!strlen(optarg))
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
break;
case 'l': /* Log destination: s|e|o */
if ((logdst = clicon_log_opt(optarg[0])) < 0)
usage(h, argv[0]);
if (logdst == CLICON_LOG_FILE &&
strlen(optarg)>1 &&
clicon_log_file(optarg+1) < 0)
goto done;
break;
}
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
clicon_debug_init(dbg, NULL);
/*
* Register error category and error/log callbacks for netsnmp special error handling
*/
if (clixon_err_cat_reg(OE_SNMP, /* category */
h, /* handle (can be NULL) */
clixon_snmp_err_cb /* log fn */
) < 0)
goto done;
yang_init(h);
/* Find, read and parse configfile */
if (clicon_options_main(h) < 0)
goto done;
/* Now rest of options */
optind = 1;
opterr = 0;
while ((c = getopt(argc, argv, SNMP_OPTS)) != -1)
switch (c) {
case 'h' : /* help */
case 'D' : /* debug */
case 'f': /* config file */
case 'l': /* log */
break; /* see above */
case 'o':{ /* Configuration option */
char *val;
if ((val = index(optarg, '=')) == NULL)
usage(h, argv0);
*val++ = '\0';
if (clicon_option_add(h, optarg, val) < 0)
goto done;
break;
}
case 'z': /* Zap other process */
zap++;
break;
default:
usage(h, argv[0]);
break;
}
argc -= optind;
argv += optind;
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);
/* Check pid-file, if zap kill the old daemon, else return here */
if ((pidfile = clicon_snmp_pidfile(h)) == NULL){
clicon_err(OE_FATAL, 0, "pidfile not set");
goto done;
}
if (pidfile_get(pidfile, &pid) < 0)
goto done;
if (zap){
if (pid && pidfile_zapold(pid) < 0)
return -1;
if (lstat(pidfile, &st) == 0)
unlink(pidfile);
snmp_terminate(h);
exit(0); /* OK */
}
else if (pid){
clicon_err(OE_DAEMON, 0, "Clixon_snmp daemon already running with pid %d\n(Try killing it with %s -z)",
pid, argv0);
return -1; /* goto done deletes pidfile */
}
/* Here there is either no old process or we have killed it,.. */
if (lstat(pidfile, &st) == 0)
unlink(pidfile);
/* Init cligen buffers */
cligen_buflen = clicon_option_int(h, "CLICON_CLI_BUF_START");
cligen_bufthreshold = clicon_option_int(h, "CLICON_CLI_BUF_THRESHOLD");
cbuf_alloc_set(cligen_buflen, cligen_bufthreshold);
if ((sz = clicon_option_int(h, "CLICON_LOG_STRING_LIMIT")) != 0)
clicon_log_string_limit_set(sz);
/* Set default namespace according to CLICON_NAMESPACE_NETCONF_DEFAULT */
xml_nsctx_namespace_netconf_default(h);
/* Add (hardcoded) netconf features in case ietf-netconf loaded here
* Otherwise it is loaded in netconf_module_load below
*/
if (netconf_module_features(h) < 0)
goto done;
/* Create top-level yang spec and store as option */
if ((yspec = yspec_new()) == NULL)
goto done;
clicon_dbspec_yang_set(h, yspec);
/* Load Yang modules
* 1. Load a yang module as a specific absolute filename */
if ((str = clicon_yang_main_file(h)) != NULL){
if (yang_spec_parse_file(h, str, yspec) < 0)
goto done;
}
/* 2. Load a (single) main module */
if ((str = clicon_yang_module_main(h)) != NULL){
if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
yspec) < 0)
goto done;
}
/* 3. Load all modules in a directory */
if ((str = clicon_yang_main_dir(h)) != NULL){
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
/* Load clixon lib yang module */
if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
/* Add netconf yang spec, used by netconf client and as internal protocol */
if (netconf_module_load(h) < 0)
goto done;
/* Here all modules are loaded
* Compute and set canonical namespace context
*/
if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0)
goto done;
if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done;
if (dbg)
clicon_option_dump(h, dbg);
/* Get session id from backend hello */
clicon_session_id_set(h, getpid());
/* Send hello request to backend to get session-id back
* This is done once at the beginning of the session and then this is
* used by the client, even though new TCP sessions are created for
* each message sent to the backend.
*/
if (clicon_hello_req(h, &id) < 0)
goto done;
clicon_session_id_set(h, id);
/* Init snmp as subagent */
if (clixon_snmp_init_subagent(h, logdst) < 0)
goto done;
/* Init and traverse mib-translated yangs and register callbacks */
if (clixon_snmp_traverse_mibyangs(h) < 0)
goto done;
/* Write pid-file */
if (pidfile_write(pidfile) < 0)
goto done;
/* main event loop */
if (clixon_event_loop(h) < 0)
goto done;
retval = 0;
done:
snmp_terminate(h);
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
clicon_log(LOG_NOTICE, "%s: %u Terminated", __PROGRAM__, getpid());
return retval;
}

532
apps/snmp/snmp_register.c Normal file
View file

@ -0,0 +1,532 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
Sponsored by Siklu Communications LTD
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* See RFC 6643
* Extensions are grouped in some categories, the one I have seen are, example:
* 1. leaf
* smiv2:max-access "read-write";
* smiv2:oid "1.3.6.1.4.1.8072.2.1.1";
* smiv2:defval "42"; (not always)
* 2. container, list
* smiv2:oid "1.3.6.1.4.1.8072.2.1";
* 3. module level
* smiv2:alias "netSnmpExamples" {
* smiv2:oid "1.3.6.1.4.1.8072.2";
*
* SNMP messages:
* 160 MODE_GETNEXT / SNMP_MSG_GET
* 161 MODE_GET / SNMP_MSG_GETNEXT
* 0 MODE_SET_RESERVE1
* 1 MODE_SET_RESERVE2
* 2 MODE_SET_ACTION
* 3 MODE_SET_COMMIT
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <syslog.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
#include <assert.h>
/* net-snmp */
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
#include "snmp_lib.h"
#include "snmp_register.h"
#include "snmp_handler.h"
/*! Parse smiv2 extensions for YANG leaf
* Typical leaf:
* smiv2:oid "1.3.6.1.4.1.8072.2.1.1";
* smiv2:max-access "read-write";
* smiv2:defval "42"; (optional)
* @param[in] h Clixon handle
* @param[in] ys Mib-Yang node
* @param[in] cvk_orig Vector of untranslated key/index values (eg "foo")
* @param[in] oidk Part of OID thatrepresents key
* @param[in] oidklen Length of oidk
* @retval 0 OK
* @retval -1 Error
* netsnmp_subtree_find(oid1,sz1, 0, 0)
*/
static int
mibyang_leaf_register(clicon_handle h,
yang_stmt *ys,
cvec *cvk_val,
oid *oidk,
size_t oidklen)
{
int retval = -1;
netsnmp_handler_registration *nhreg = NULL;
netsnmp_mib_handler *handler;
int ret;
char *modes_str = NULL;
char *default_str = NULL;
oid oid1[MAX_OID_LEN] = {0,};
size_t oid1len = MAX_OID_LEN;
int modes;
char *name;
clixon_snmp_handle *sh;
cbuf *cboid = NULL;
if ((cboid = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if ((ret = yangext_oid_get(ys, oid1, &oid1len, NULL)) < 0)
goto done;
if (ret == 0)
goto ok;
if (oid_append(oid1, &oid1len, oidk, oidklen) < 0)
goto done;
/* Check if already registered */
if (clixon_snmp_api_oid_find(oid1, oid1len) == 1)
goto ok;
if (yang_extension_value(ys, "max-access", IETF_YANG_SMIV2_NS, NULL, &modes_str) < 0)
goto done;
/* Only for sanity check of types initially to fail early */
if (type_yang2asn1(ys, NULL, 0) < 0)
goto done;
/* Get modes (access) read-only, read-write, not-accessible, accessible-for-notify
*/
if (modes_str == NULL)
goto ok;
modes = snmp_access_str2int(modes_str);
/* SMI default value, How is this different from yang defaults?
*/
if (yang_extension_value(ys, "defval", IETF_YANG_SMIV2_NS, NULL, &default_str) < 0)
goto done;
name = yang_argument_get(ys);
/* Stateless function, just returns ptr */
if ((handler = netsnmp_create_handler(name, clixon_snmp_scalar_handler)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_create_handler");
goto done;
}
/* Userdata to pass around in netsmp callbacks
* XXX: not deallocated
*/
if ((sh = malloc(sizeof(*sh))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(sh, 0, sizeof(*sh));
sh->sh_h = h;
sh->sh_ys = ys;
memcpy(sh->sh_oid, oid1, sizeof(oid1));
sh->sh_oidlen = oid1len;
sh->sh_default = default_str;
if (cvk_val &&
(sh->sh_cvk_orig = cvec_dup(cvk_val)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
/* Stateless function, just returns ptr */
if ((nhreg = netsnmp_handler_registration_create(name, handler,
oid1, oid1len,
modes)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_handler_registration_create");
netsnmp_handler_free(handler);
goto done;
}
/* Register our application data and how to free it */
handler->myvoid = (void*)sh;
handler->data_clone = snmp_handle_clone;
handler->data_free = snmp_handle_free;
/*
* XXX: nhreg->agent_data
*/
if ((ret = netsnmp_register_instance(nhreg)) != SNMPERR_SUCCESS){
/* Note MIB_ errors, not regular SNMPERR_ */
clicon_err(OE_SNMP, ret-CLIXON_ERR_SNMP_MIB, "netsnmp_register_instance");
goto done;
}
oid_cbuf(cboid, oid1, oid1len);
clicon_debug(1, "%s register: %s %s", __FUNCTION__, name, cbuf_get(cboid));
ok:
retval = 0;
done:
if (cboid)
cbuf_free(cboid);
return retval;
}
/*! Register table entry handler itself (not column/row leafs)
*
* Parse smiv2 extensions for YANG container/list
*
* Typical table:
* container x {
* smiv2:oid "1.3.6.1.4.1.8072.2.2.1";
* list y{
*
* }
* }
* @param[in] h Clixon handle
* @param[in] ys Mib-Yang node (container)
* @param[in] yl Mib-Yang node (list)
* @retval 0 OK
* @retval -1 Error
*/
static int
mibyang_table_register(clicon_handle h,
yang_stmt *ylist)
{
int retval = -1;
netsnmp_handler_registration *nhreg;
char *oidstr = NULL;
oid oid1[MAX_OID_LEN] = {0,};
size_t oid1len = MAX_OID_LEN;
char *name;
clixon_snmp_handle *sh;
int ret;
netsnmp_mib_handler *handler;
netsnmp_table_registration_info *table_info = NULL;
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *keyname;
yang_stmt *yleaf;
int asn1type;
yang_stmt *ys;
if ((ys = yang_parent_get(ylist)) == NULL ||
yang_keyword_get(ys) != Y_CONTAINER){
clicon_err(OE_YANG, EINVAL, "ylist parent is not list");
goto done;
}
/* Get OID from parent container */
if ((ret = yangext_oid_get(ys, oid1, &oid1len, &oidstr)) < 0)
goto done;
if (ret == 0)
goto ok;
name = yang_argument_get(ys);
/* Userdata to pass around in netsmp callbacks
* XXX: not deallocated
*/
if ((sh = malloc(sizeof(*sh))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(sh, 0, sizeof(*sh));
sh->sh_h = h;
sh->sh_ys = ylist;
memcpy(sh->sh_oid, oid1, sizeof(oid1));
sh->sh_oidlen = oid1len;
if ((handler = netsnmp_create_handler(name, clixon_snmp_table_handler)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_create_handler");
goto done;
}
if ((nhreg = netsnmp_handler_registration_create(name, handler,
oid1, oid1len,
HANDLER_CAN_RWRITE)) == NULL){
clicon_err(OE_XML, errno, "netsnmp_handler_registration_create");
netsnmp_handler_free(handler);
goto done;
}
/* Register our application data and how to free it */
handler->myvoid =(void*)sh;
handler->data_clone = snmp_handle_clone;
handler->data_free = snmp_handle_free;
/* See netsnmp_register_table_data_set */
if ((table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL){
clicon_err(OE_UNIX, errno, "SNMP_MALLOC_TYPEDEF");
goto done;
}
/* Keys, go through keys */
if ((cvk = yang_cvec_get(ylist)) == NULL){
clicon_err(OE_YANG, 0, "No keys");
goto done;
}
cvi = NULL;
/* Iterate over individual keys */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((yleaf = yang_find(ylist, Y_LEAF, keyname)) == NULL){
clicon_err(OE_XML, 0, "List statement \"%s\" has no key leaf \"%s\"",
yang_argument_get(ylist), keyname);
goto done;
}
if (type_yang2asn1(yleaf, &asn1type, 0) < 0)
// goto done;
goto ok; // XXX skip
if (snmp_varlist_add_variable(&table_info->indexes,
NULL, // oid name
0, // oid len
asn1type,
NULL, // value
0) == NULL){
clicon_err(OE_XML, errno, "snmp_varlist_add_variable");
goto done;
}
}
table_info->min_column = 1;
/* Count columns */
yleaf = NULL;
table_info->max_column = 0;
while ((yleaf = yn_each(ylist, yleaf)) != NULL) {
if (yang_keyword_get(yleaf) == Y_LEAF)
table_info->max_column++;
}
if ((ret = netsnmp_register_table(nhreg, table_info)) != SNMPERR_SUCCESS){
clicon_err(OE_SNMP, ret, "netsnmp_register_table");
goto done;
}
sh->sh_table_info = table_info; /* Keep to free at exit */
clicon_debug(1, "%s register: %s %s", __FUNCTION__, name, oidstr);
ok:
retval = 0;
done:
return retval;
}
/*! Register table sub-oid:s of existing entries in clixon
* This assumes a table contains a set of keys and a list of leafs only
* The function makes a query to the datastore and registers all table entries that
* currently exists. This means it registers for a static table. If new rows or columns
* are created or deleted this will not change the OID registration.
* That is, the table registration is STATIC
* @param[in] h Clixon handle
* @param[in] ys Mib-Yang node (container)
* @param[in] ylist Mib-Yang node (list)
* @retval 0 OK
* @retval -1 Error
*/
int
mibyang_table_poll(clicon_handle h,
yang_stmt *ylist)
{
int retval = -1;
cvec *nsc = NULL;
char *xpath = NULL;
cxobj *xt = NULL;
cxobj *xerr;
cxobj *xtable;
cxobj *xrow;
cxobj *xcol;
yang_stmt *y;
cvec *cvk_name;
cvec *cvk_val = NULL; /* vector of index keys: original index */
yang_stmt *ys;
int ret;
oid oidk[MAX_OID_LEN] = {0,};
size_t oidklen = MAX_OID_LEN;
clicon_debug(1, "%s", __FUNCTION__);
if ((ys = yang_parent_get(ylist)) == NULL ||
yang_keyword_get(ys) != Y_CONTAINER){
clicon_err(OE_YANG, EINVAL, "ylist parent is not list");
goto done;
}
if (xml_nsctx_yang(ys, &nsc) < 0)
goto done;
if (snmp_yang2xpath(ys, NULL, &xpath) < 0)
goto done;
if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, &xt) < 0)
goto done;
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
clixon_netconf_error(xerr, "clicon_rpc_get", NULL);
goto done;
}
if ((xtable = xpath_first(xt, nsc, "%s", xpath)) != NULL) {
/* Make a clone of key-list, but replace names with values */
if ((cvk_name = yang_cvec_get(ylist)) == NULL){
clicon_err(OE_YANG, 0, "No keys");
goto done;
}
xrow = NULL;
while ((xrow = xml_child_each(xtable, xrow, CX_ELMNT)) != NULL) {
if ((ret = snmp_xmlkey2val_oid(xrow, cvk_name, &cvk_val, oidk, &oidklen)) < 0)
goto done;
if (ret == 0)
continue; /* skip row, not all indexes */
xcol = NULL;
while ((xcol = xml_child_each(xrow, xcol, CX_ELMNT)) != NULL) {
if ((y = xml_spec(xcol)) == NULL)
continue;
if (mibyang_leaf_register(h, y, cvk_val, oidk, oidklen) < 0)
goto done;
}
}
}
retval = 0;
done:
if (xpath)
free(xpath);
if (cvk_val)
cvec_free(cvk_val);
if (xt)
xml_free(xt);
if (nsc)
xml_nsctx_free(nsc);
return retval;
}
/*! Traverse mib-yang tree, identify scalars and tables, register OID and callbacks
*
* The tree is traversed depth-first, which at least guarantees that a parent is
* traversed before a child.
* Extensions are grouped in some categories, the one I have seen are, example:
* 1. leaf
* smiv2:max-access "read-write";
* smiv2:oid "1.3.6.1.4.1.8072.2.1.1";
* smiv2:defval "42"; (not always)
* 2. container, list
* smiv2:oid "1.3.6.1.4.1.8072.2.1";
* 3. module level
* smiv2:alias "netSnmpExamples" {
* smiv2:oid "1.3.6.1.4.1.8072.2";
* @param[in] h Clixon handle
* @param[in] yn yang node
* @param[in] table Yang node is within table
* @retval 0 OK, all nodes traversed
* @retval -1 Error, aborted at first error encounter
*/
static int
mibyang_traverse(clicon_handle h,
yang_stmt *yn)
{
int retval = -1;
yang_stmt *ys = NULL;
yang_stmt *yp;
int ret;
clicon_debug(1, "%s %s", __FUNCTION__, yang_argument_get(yn));
switch(yang_keyword_get(yn)){
case Y_LEAF:
if (mibyang_leaf_register(h, yn, NULL, NULL, 0) < 0)
goto done;
break;
case Y_CONTAINER: /* See list case */
break;
case Y_LIST: /* If parent is container -> identify as table */
yp = yang_parent_get(yn);
if (yang_keyword_get(yp) == Y_CONTAINER){
/* Register table entry handler itself (not column/row leafs) */
if (mibyang_table_register(h, yn) < 0)
goto done;
goto ok;
}
break;
default:
break;
}
/* Traverse data nodes in tree (module is special case */
ys = NULL;
while ((ys = yn_each(yn, ys)) != NULL) {
if (!yang_schemanode(ys))
continue;
if ((ret = mibyang_traverse(h, ys)) < 0)
goto done;
if (ret > 0){
retval = ret;
goto done;
}
}
ok:
retval = 0;
done:
return retval;
}
/*! Init mib-translated yangs and register callbacks by traversing the yang
*
* @þaram[in] h Clixon handle
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_snmp_traverse_mibyangs(clicon_handle h)
{
int retval = -1;
char *modname;
cxobj *x;
yang_stmt *yspec;
yang_stmt *ymod;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
/* Loop over clixon configuration file to find all CLICON_SNMP_MIB, and
* then loop over all those MIBs to register OIDs with netsnmp
*/
x = NULL;
while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x), "CLICON_SNMP_MIB") != 0)
continue;
if ((modname = xml_body(x)) == NULL)
continue;
clicon_debug(1, "%s %s: \"%s\"", __FUNCTION__, xml_name(x), modname);
/* Note, here we assume the Yang is loaded by some other mechanism and
* error if it not found.
* Alternatively, that YANG could be loaded.
* Problem is, if clixon_snmp has not loaded it, has backend done it?
* What happens if backend has not loaded it?
*/
if ((ymod = yang_find(yspec, Y_MODULE, modname)) == NULL){
clicon_err(OE_YANG, 0, "Mib-translated-yang %s not loaded", modname);
goto done;
}
/* Recursively traverse the mib-yang to find extensions */
if (mibyang_traverse(h, ymod) < 0)
goto done;
}
retval = 0;
done:
return retval;
}

55
apps/snmp/snmp_register.h Normal file
View file

@ -0,0 +1,55 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Kristofer Hallin
Sponsored by Siklu Communications LTD
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _SNMP_REGISTER_H_
#define _SNMP_REGISTER_H_
/*
* Prototypes
*/
int mibyang_table_poll(clicon_handle h, yang_stmt *ylist);
int clixon_snmp_traverse_mibyangs(clicon_handle h);
#endif /* _SNMP_REGISTER_H_ */
#ifdef __cplusplus
} /* extern "C" */
#endif