Merge branch 'snmp'
This commit is contained in:
commit
1d78241115
47 changed files with 8494 additions and 46 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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
134
apps/snmp/Makefile.in
Normal 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
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
61
apps/snmp/snmp_handler.h
Normal 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
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
117
apps/snmp/snmp_lib.h
Normal 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
550
apps/snmp/snmp_main.c
Normal 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
532
apps/snmp/snmp_register.c
Normal 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
55
apps/snmp/snmp_register.h
Normal 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
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue