Inital commit

This commit is contained in:
Olof hagsand 2016-02-22 22:17:30 +01:00
parent edc5e091bb
commit d6e393ea58
145 changed files with 58117 additions and 0 deletions

122
apps/netconf/Makefile.in Normal file
View file

@ -0,0 +1,122 @@
#
# Makefile
#
# Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
#
# This file is part of CLICON.
#
# CLICON is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# CLICON is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with CLICON; see the file COPYING. If not, see
# <http://www.gnu.org/licenses/>.
#
#
VPATH = @srcdir@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
CC = @CC@
CFLAGS = @CFLAGS@
LDFLAGS = @LDFLAGS@
prefix = @prefix@
datarootdir = @datarootdir@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
mandir = @mandir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
sysconfdir = @sysconfdir@
includedir = @includedir@
SH_SUFFIX = @SH_SUFFIX@
CLICON_MAJOR = @CLICON_VERSION_MAJOR@
CLICON_MINOR = @CLICON_VERSION_MINOR@
# Use this clicon lib for linking
CLICON_LIB = libclicon.so.$(CLICON_MAJOR).$(CLICON_MINOR)
# For dependency
LIBDEPS = $(top_srcdir)/lib/src/$(CLICON_LIB)
LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLICON_LIB)
CPPFLAGS = @CPPFLAGS@ -fPIC
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
APPL = clicon_netconf
SRC = netconf_main.c
OBJS = $(SRC:.c=.o)
MYNAME = clicon_netconf
MYLIBLINK = lib$(MYNAME)$(SH_SUFFIX)
MYLIB = $(MYLIBLINK).$(CLICON_MAJOR).$(CLICON_MINOR)
MYLIBSO = $(MYLIBLINK).$(CLICON_MAJOR)
LIBSRC = netconf_hello.c netconf_rpc.c netconf_filter.c netconf_lib.c netconf_plugin.c
LIBOBJS = $(LIBSRC:.c=.o)
all: $(MYLIB) $(APPL)
clean:
rm -f $(OBJS) $(LIBOBJS) *.core $(APPL) $(MYLIB) $(MYLIBSO) $(MYLIBLINK)
distclean: clean
rm -f Makefile *~ .depend
# Put demon in bin
# Put other executables in libexec/
# Also create a libexec/ directory for writeable/temporary files.
# Put config file in etc/
install: install-lib $(APPL)
install -d $(DESTDIR)$(bindir)
install $(APPL) $(DESTDIR)$(bindir)
install-lib: $(MYLIB)
install -d $(DESTDIR)$(libdir)
install $(MYLIB) $(DESTDIR)$(libdir)
ln -sf $(MYLIB) $(DESTDIR)$(libdir)/$(MYLIBSO) # -l:libclicon_netconf.so.2
ln -sf $(MYLIBSO) $(DESTDIR)$(libdir)/$(MYLIBLINK) # -l:libclicon_netconf.so
install-include: clicon_netconf.h
install -d $(DESTDIR)$(includedir)/clicon
install -m 644 $^ $(DESTDIR)$(includedir)/clicon
uninstall:
rm -f $(bindir)/$(APPL)
rm -f $(libdir)/$(MYLIB)
rm -f $(includedir)/clicon/*
.SUFFIXES:
.SUFFIXES: .c .o
.c.o:
$(CC) $(INCLUDES) $(CPPFLAGS) -D__PROGRAM__=\"$(APPL)\" $(CFLAGS) -c $<
$(APPL) : $(OBJS) $(MYLIBLINK) $(LIBDEPS)
$(CC) $(LDFLAGS) $(OBJS) -L. -l:$(MYLIB) $(LIBS) -o $@
$(MYLIB) : $(LIBOBJS)
$(CC) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(LIBOBJS) $(LIBS) -Wl,-soname=$(MYLIBSO)
# link-name is needed for application linking, eg for clicon_cli and clicon_config
$(MYLIBLINK) : $(MYLIB)
# ln -sf $(MYLIB) $(MYLIBSO)
# ln -sf $(MYLIB) $@
TAGS:
find . -name '*.[chyl]' -print | etags -
depend:
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(SRC) $(APPSRC) > .depend
#include .depend

View file

@ -0,0 +1,73 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*
* The exported interface to plugins. External apps (eg frontend netconf plugins)
* should only include this file (not the netconf_*.h)
*/
#ifndef _CLICON_NETCONF_H_
#define _CLICON_NETCONF_H_
/*
* Types
*/
typedef int (*netconf_cb_t)(
clicon_handle h,
cxobj *xorig, /* Original request. */
cxobj *xn, /* Sub-tree (under xorig) at child: <rpc><xn></rpc> */
cbuf *cb, /* Output xml stream. For reply */
cbuf *cb_err, /* Error xml stream. For error reply */
void *arg /* Argument given at netconf_register_callback() */
);
/*
* Prototypes
* (Duplicated. Also in netconf_*.h)
*/
int netconf_output(int s, cbuf *xf, char *msg);
int netconf_create_rpc_reply(cbuf *cb, /* msg buffer */
cxobj *xr, /* orig request */
char *body,
int ok);
int netconf_register_callback(clicon_handle h,
netconf_cb_t cb, /* Callback called */
void *arg, /* Arg to send to callback */
char *tag); /* Xml tag when callback is made */
int netconf_create_rpc_error(cbuf *xf, /* msg buffer */
cxobj *xr, /* orig request */
char *tag,
char *type,
char *severity,
char *message,
char *info);
void netconf_ok_set(int ok);
int netconf_ok_get(void);
int netconf_xpath(cxobj *xsearch,
cxobj *xfilter,
cbuf *xf, cbuf *xf_err,
cxobj *xt);
#endif /* _CLICON_NETCONF_H_ */

View file

@ -0,0 +1,633 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*
* netconf match & selection: get and edit operations
*****************************************************************************/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clicon/clicon.h>
#include "netconf_rpc.h"
#include "netconf_lib.h"
#include "netconf_filter.h"
/*
* xml_filter
* xf specifices a filter, and xn is an xml tree.
* Select the part of xn that matches xf and return it.
* Change xn destructively by removing the parts of the sub-tree that does
* not match.
* Match according to Section 6 of RFC 4741.
NO_FILTER, select all
EMPTY_FILTER, select nothing
ATTRIBUTE_MATCH, select if attribute match
SELECTION, select this node
CONTENT_MATCH, select all siblings with matching content
CONTAINMENT select
*/
/* return a string containing leafs value, NULL if no leaf or no value */
static char*
leafstring(cxobj *x)
{
cxobj *c;
if (xml_type(x) != CX_ELMNT)
return NULL;
if (xml_child_nr(x) != 1)
return NULL;
c = xml_child_i(x, 0);
if (xml_child_nr(c) != 0)
return NULL;
if (xml_type(c) != CX_BODY)
return NULL;
return xml_value(c);
}
/*! Internal recursive part where configuration xml tree is pruned frim filter
* assume parent has been selected and filter match (same name) as parent
* parent is pruned according to selection.
* @param[in] xfilter Filter xml
* @param[out] xconf Configuration xml
* @retval 0 OK
* @retval -1 Error
*/
static int
xml_filter2(cxobj *xfilter,
cxobj *xparent,
int *remove_me)
{
cxobj *s;
cxobj *sprev;
cxobj *f;
cxobj *attr;
char *an;
char *af;
char *fstr;
char *sstr;
int containments;
int remove_s;
*remove_me = 0;
assert(xfilter && xparent && strcmp(xml_name(xfilter), xml_name(xparent))==0);
/* 1. Check selection */
if (xml_child_nr(xfilter) == 0)
goto match;
/* Count containment/selection nodes in filter */
f = NULL;
containments = 0;
while ((f = xml_child_each(xfilter, f, CX_ELMNT)) != NULL) {
if (leafstring(f))
continue;
containments++;
}
/* 2. Check attribute match */
attr = NULL;
while ((attr = xml_child_each(xfilter, attr, CX_ATTR)) != NULL) {
af = xml_value(attr);
an = xml_find_value(xfilter, xml_name(attr));
if (af && an && strcmp(af, an)==0)
; // match
else
goto nomatch;
}
/* 3. Check content match */
f = NULL;
while ((f = xml_child_each(xfilter, f, CX_ELMNT)) != NULL) {
if ((fstr = leafstring(f)) == NULL)
continue;
if ((s = xml_find(xparent, xml_name(f))) == NULL)
goto nomatch;
if ((sstr = leafstring(s)) == NULL)
continue;
if (strcmp(fstr, sstr))
goto nomatch;
}
/* If filter has no further specifiers, accept */
if (!containments)
goto match;
/* Check recursively the rest of the siblings */
sprev = s = NULL;
while ((s = xml_child_each(xparent, s, CX_ELMNT)) != NULL) {
if ((f = xml_find(xfilter, xml_name(s))) == NULL){
xml_prune(xparent, s, 1);
s = sprev;
continue;
}
if (leafstring(f)){
sprev = s;
continue; // unsure?sk=lf
}
// XXX: s can be removed itself in the recursive call !
remove_s = 0;
if (xml_filter2(f, s, &remove_s) < 0)
return -1;
if (remove_s){
xml_prune(xparent, s, 1);
s = sprev;
}
sprev = s;
}
match:
return 0;
nomatch: /* prune this parent node (maybe only children?) */
*remove_me = 1;
return 0;
}
/*! Remove parts of configuration xml tree that does not match filter xml tree
* @param[in] xfilter Filter xml
* @param[out] xconf Configuration xml
* @retval 0 OK
* @retval -1 Error
* This is the top-level function, calls a recursive variant.
*/
int
xml_filter(cxobj *xfilter,
cxobj *xconfig)
{
int retval;
int remove_s;
/* Call recursive variant */
retval = xml_filter2(xfilter,
xconfig,
&remove_s);
return retval;
}
/*
* netconf_xpath
* @param[in] xsearch where you search for xpath, grouped by a single top node which
* is not significant and will not be returned in any result.
* @param[in] xfilter the xml sub-tree, eg:
* <filter type="xpath" select="/t:top/t:users/t:user[t:name='fred']"/>
* @param[out] cb Output xml stream. For reply
* @param[out] cb_err Error xml stream. For error reply
* @param[in] xorig Original tree
*
*/
int
netconf_xpath(cxobj *xsearch,
cxobj *xfilter,
cbuf *cb,
cbuf *cb_err,
cxobj *xorig)
{
cxobj *x;
int retval = -1;
char *selector;
cxobj **xv;
int xlen;
int i;
if ((selector = xml_find_value(xfilter, "select")) == NULL){
netconf_create_rpc_error(cb_err, xorig,
"operation-failed",
"application",
"error",
NULL,
"select");
goto done;
}
x = NULL;
clicon_errno = 0;
if ((xv = xpath_vec(xsearch, selector, &xlen)) != NULL) {
for (i=0; i<xlen; i++){
x = xv[i];
clicon_xml2cbuf(cb, x, 0, 1);
}
free(xv);
}
/* XXX: NULL means error sometimes */
if (clicon_errno){
netconf_create_rpc_error(cb_err, xorig,
"operation-failed",
"application",
"error",
clicon_err_reason,
"select");
goto done;
}
retval = 0;
done:
return retval;
}
#ifdef NOTUSED
/*
* in edit_config, we copy a tree to the config. But some wthings shouldbe
* cleaned:
* - operation attribute
*/
static int
netconf_clean(cxobj *xn)
{
cxobj *xa;
cxobj *x;
if ((xa = xml_find(xn, "operation")) != NULL &&
xml_type(xa) == CX_ATTR)
xml_prune(xn, xa, 1);
x = NULL;
while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL)
netconf_clean(x);
return 0;
}
/*
* get_operation
* get the value of the "operation" attribute and change op if given
*/
static int
get_operation(cxobj *xn,
enum operation_type *op,
cbuf *xf_err,
cxobj *xorig)
{
char *opstr;
if ((opstr = xml_find_value(xn, "operation")) != NULL){
if (strcmp("merge", opstr) == 0)
*op = OP_MERGE;
else
if (strcmp("replace", opstr) == 0)
*op = OP_REPLACE;
else
if (strcmp("create", opstr) == 0)
*op = OP_CREATE;
else
if (strcmp("delete", opstr) == 0)
*op = OP_DELETE;
else
if (strcmp("remove", opstr) == 0)
*op = OP_REMOVE;
else{
netconf_create_rpc_error(xf_err, xorig,
"bad-attribute",
"protocol",
"error",
NULL,
"<bad-attribute>operation</bad-attribute>");
return -1;
}
}
return 0;
}
/* forward */
static int
xml_edit(cxobj *filter,
cxobj *parent,
enum operation_type op,
cbuf *xf_err,
cxobj *xorig);
/*! Merge two XML trees according to RFC4741/Junos
* 1. in configuration(parent) but not in new(filter) -> remain in configuration
* 2. not in configuration but in new -> add to configuration
* 3. Both in configuration and new: Do 1.2.3 with children.
* A key is: the configuration data identified by the element
*/
static int
edit_selection(cxobj *filter,
cxobj *parent,
enum operation_type op,
cbuf *xf_err,
cxobj *xorig)
{
int retval = -1;
assert(filter && parent && strcmp(xml_name(filter), xml_name(parent))==0);
fprintf(stderr, "%s: %s\n", __FUNCTION__, xml_name(filter));
switch (op){
case OP_MERGE:
break;
case OP_REPLACE:
xml_prune(xml_parent(parent), parent, 1);
break;
case OP_CREATE:
netconf_create_rpc_error(xf_err, xorig,
NULL,
"protocol",
"error",
"statement creation failed",
"<bad-element></bad-element>");
goto done;
break;
case OP_DELETE:
fprintf(stderr, "%s: %s DELETE\n", __FUNCTION__, xml_name(filter));
if (xml_child_nr(parent) == 0){
fprintf(stderr, "%s: %s ERROR\n", __FUNCTION__, xml_name(filter));
netconf_create_rpc_error(xf_err, xorig,
NULL,
"protocol",
"error",
"statement not found",
"<bad-element></bad-element>");
goto done;
}
/* fall through */
case OP_REMOVE:
fprintf(stderr, "%s: %s REMOVE\n", __FUNCTION__, xml_name(filter));
xml_prune(xml_parent(parent), parent, 1);
break;
case OP_NONE:
break;
}
retval = 0;
netconf_ok_set(1); /* maybe cc_ok shouldnt be set if we continue? */
done:
return retval;
}
/*
* XXX: not called from external?
*/
static int
edit_match(cxobj *filter,
cxobj *parent,
enum operation_type op,
cbuf *xf_err,
cxobj *xorig,
int match
)
{
cxobj *f;
cxobj *s;
cxobj *copy;
int retval = -1;
clicon_debug(1, "%s: %s op:%d", __FUNCTION__, xml_name(filter), op);
if (match)
switch (op){
case OP_REPLACE:
case OP_CREATE:
s = NULL;
while ((s = xml_child_each(parent, s, -1)) != NULL){
xml_prune(parent, s, 1);
s = NULL;
}
if (xml_copy(filter, parent) < 0)
goto done;
netconf_clean(parent);
retval = 0;
netconf_ok_set(1);
goto done;
break;
case OP_DELETE:
case OP_REMOVE:
xml_prune(xml_parent(parent), parent, 1);
netconf_ok_set(1);
goto done;
break;
case OP_MERGE:
case OP_NONE:
break;
}
f = NULL;
while ((f = xml_child_each(filter, f, CX_ELMNT)) != NULL) {
s = xml_find(parent, xml_name(f));
switch (op){
case OP_MERGE:
/* things in filter:
not in conf should be added
in conf go down recursive
*/
if (s == NULL && match){
if ((copy = xml_new(xml_name(f), parent)) == NULL)
goto done;
if (xml_copy(f, copy) < 0)
goto done;
netconf_clean(copy);
}
else{
s = NULL;
while ((s = xml_child_each(parent, s, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(f), xml_name(s)))
continue;
if ((retval = xml_edit(f, s, op, xf_err, xorig)) < 0)
goto done;
}
}
break;
case OP_REPLACE:
#if 1
/* things in filter
in conf: remove from conf and
add to conf
*/
// if (!match)
// break;
if (s != NULL)
xml_prune(parent, s, 1);
if ((copy = xml_new(xml_name(f), parent)) == NULL)
goto done;
if (xml_copy(f, copy) < 0)
goto done;
netconf_clean(copy);
#endif
break;
case OP_CREATE:
#if 0
/* things in filter
in conf: error
else add to conf
*/
if (!match)
break;
if (s != NULL){
netconf_create_rpc_error(xf_err, xorig,
NULL,
"protocol",
"error",
"statement creation failed",
"<bad-element></bad-element>");
goto done;
}
if ((copy = xml_new(xml_name(f), parent)) == NULL)
goto done;
if (xml_copy(f, copy) < 0)
goto done;
netconf_clean(copy);
#endif
break;
case OP_DELETE:
/* things in filter
if not in conf: error
else remove from conf
*/
#if 0
if (!match)
break;
if (s == NULL){
netconf_create_rpc_error(xf_err, xorig,
NULL,
"protocol",
"error",
"statement not found",
"<bad-element></bad-element>");
goto done;
}
#endif
/* fall through */
case OP_REMOVE:
/* things in filter
remove from conf
*/
#if 0
if (!match)
break;
xml_prune(parent, s, 1);
#endif
break;
case OP_NONE:
/* recursive descent */
s = NULL;
while ((s = xml_child_each(parent, s, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(f), xml_name(s)))
continue;
if ((retval = xml_edit(f, s, op, xf_err, xorig)) < 0)
goto done;
}
break;
}
} /* while f */
retval = 0;
netconf_ok_set(1); /* maybe cc_ok shouldnt be set if we continue? */
done:
return retval;
}
/*
* xml_edit
* merge filter into parent
* XXX: not called from external?
*/
static int
xml_edit(cxobj *filter,
cxobj *parent,
enum operation_type op,
cbuf *xf_err,
cxobj *xorig)
{
cxobj *attr;
cxobj *f;
cxobj *s;
int retval = -1;
char *an, *af;
char *fstr, *sstr;
int keymatch = 0;
get_operation(filter, &op, xf_err, xorig);
/* 1. First try selection: filter is empty */
if (xml_child_nr(filter) == 0){ /* no elements? */
retval = edit_selection(filter, parent, op, xf_err, xorig);
goto done;
}
if (xml_child_nr(filter) == 1 && /* same as above */
xpath_first(filter, "/[@operation]")){
retval = edit_selection(filter, parent, op, xf_err, xorig);
goto done;
}
/* 2. Check attribute match */
attr = NULL;
while ((attr = xml_child_each(filter, attr, CX_ATTR)) != NULL) {
af = xml_value(attr);
an = xml_find_value(filter, xml_name(attr));
if (af && an && strcmp(af, an)==0)
; // match
else
goto nomatch;
}
/* 3. Check content match */
/*
* For content-match we do a somewhat strange thing, we find
* a match in first content-node and assume that is unique
* and then we remove/replace that
* For merge we just continue
*/
f = NULL;
while ((f = xml_child_each(filter, f, CX_ELMNT)) != NULL) {
if ((fstr = leafstring(f)) == NULL)
continue;
/* we found a filter leaf-match: no return we say it should match*/
if ((s = xml_find(parent, xml_name(f))) == NULL)
goto nomatch;
if ((sstr = leafstring(s)) == NULL)
goto nomatch;
if (strcmp(fstr, sstr))
goto nomatch;
keymatch++;
break; /* match */
}
if (debug && keymatch){
fprintf(stderr, "%s: match\n", __FUNCTION__);
fprintf(stderr, "%s: filter:\n", __FUNCTION__);
clicon_xml2file(stdout, filter, 0, 1);
fprintf(stderr, "%s: config:\n", __FUNCTION__);
clicon_xml2file(stdout, parent, 0, 1);
}
retval = edit_match(filter, parent, op, xf_err, xorig, keymatch);
/* match */
netconf_ok_set(1);
retval = 0;
done:
return retval;
nomatch:
return 0;
}
#endif /* NOTUSED */

View file

@ -0,0 +1,36 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*
* netconf match & selection: get and edit operations
*****************************************************************************/
#ifndef _NETCONF_FILTER_H_
#define _NETCONF_FILTER_H_
/*
* Prototypes
*/
int xml_filter(cxobj *xf, cxobj *xn);
int netconf_xpath(cxobj *xsearch,
cxobj *xfilter,
cbuf *xf, cbuf *xf_err,
cxobj *xt);
#endif /* _NETCONF_FILTER_H_ */

View file

@ -0,0 +1,118 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*
* Code for handling netconf hello messages
*****************************************************************************/
/*
Capabilities are advertised in messages sent by each peer during
session establishment. When the NETCONF session is opened, each peer
(both client and server) MUST send a <hello> element containing a
list of that peer's capabilities. Each peer MUST send at least the
base NETCONF capability, "urn:ietf:params:netconf:base:1.0".
<hello>
<capabilities>
<capability>URI</capability>
</capabilities>
</hello>
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clicon/clicon.h>
#include "netconf_lib.h"
#include "netconf_hello.h"
static int
netconf_hello(cxobj *xn)
{
cxobj *x;
x = NULL;
while ((x = xpath_each(xn, "//capability", x)) != NULL) {
//fprintf(stderr, "cap: %s\n", xml_body(x));
}
return 0;
}
int
netconf_hello_dispatch(cxobj *xn)
{
cxobj *xp;
int retval = -1;
if ((xp = xpath_first(xn, "//hello")) != NULL)
retval = netconf_hello(xp);
return retval;
}
/*
* netconf_create_hello
* create capability string (once)
*/
int
netconf_create_hello(cbuf *xf, /* msg buffer */
int session_id)
{
int retval = 0;
add_preamble(xf);
cprintf(xf, "<hello>");
cprintf(xf, "<capabilities>");
cprintf(xf, "<capability>urn:ietf:params:xml:ns:netconf:base:1.0</capability>\n");
cprintf(xf, "<capability>urn:ietf:params:xml:ns:netconf:capability:candidate:1:0</capability>\n");
cprintf(xf, "<capability>urn:ietf:params:xml:ns:netconf:capability:validate:1.0</capability>\n");
cprintf(xf, "<capability>urn:ietf:params:netconf:capability:xpath:1.0</capability>\n");
cprintf(xf, "<capability>urn:ietf:params:netconf:capability:notification:1.0</capability>\n");
// cprintf(xf, "<capability>urn:rnr:rnrapi:1:0</capability>");
cprintf(xf, "</capabilities>");
cprintf(xf, "<session-id>%lu</session-id>", 42+session_id);
cprintf(xf, "</hello>");
add_postamble(xf);
return retval;
}

View file

@ -0,0 +1,34 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*
* Code for handling netconf hello messages
*****************************************************************************/
#ifndef _NETCONF_HELLO_H_
#define _NETCONF_HELLO_H_
/*
* Prototypes
*/
int netconf_create_hello(cbuf *xf, int session_id);
int netconf_hello_dispatch(cxobj *xn);
#endif /* _NETCONF_HELLO_H_ */

261
apps/netconf/netconf_lib.c Normal file
View file

@ -0,0 +1,261 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*
* netconf lib
*****************************************************************************/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <syslog.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clicon/clicon.h>
#include "netconf_rpc.h"
#include "netconf_lib.h"
/*
* Exported variables
*/
enum transport_type transport = NETCONF_SSH;
int cc_closed = 0;
static int cc_ok = 0;
void
netconf_ok_set(int ok)
{
cc_ok = ok;
}
int
netconf_ok_get(void)
{
return cc_ok;
}
int
add_preamble(cbuf *xf)
{
if (transport == NETCONF_SOAP)
cprintf(xf, "\n<soapenv:Envelope\n xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\">\n"
"<soapenv:Body>");
return 0;
}
/*
* add_postamble
* add netconf xml postamble of message. That is, xml after the body of the message.
* for soap this is the envelope stuff, for ssh this is ]]>]]>
*/
int
add_postamble(cbuf *xf)
{
switch (transport){
case NETCONF_SSH:
cprintf(xf, "]]>]]>"); /* Add RFC4742 end-of-message marker */
break;
case NETCONF_SOAP:
cprintf(xf, "\n</soapenv:Body>" "</soapenv:Envelope>");
break;
}
return 0;
}
/*
* add_error_preamble
* compared to regular messages (see add_preamble), error message differ in some
* protocols (eg soap) by adding a longer and deeper header.
*/
int
add_error_preamble(cbuf *xf, char *reason)
{
switch (transport){
case NETCONF_SOAP:
cprintf(xf, "<soapenv:Envelope xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\">"
"<soapenv:Body>"
"<soapenv:Fault>"
"<soapenv:Code>"
"<soapenv:Value>env:Receiver</soapenv:Value>"
"</soapenv:Code>"
"<soapenv:Reason>"
"<soapenv:Text xml:lang=\"en\">%s</soapenv:Text>"
"</soapenv:Reason>"
"<detail>", reason);
break;
default:
if (add_preamble(xf) < 0)
return -1;
break;
}
return 0;
}
/*
* add_error_postamble
* compared to regular messages (see add_postamble), error message differ in some
* protocols (eg soap) by adding a longer and deeper header.
*/
int
add_error_postamble(cbuf *xf)
{
switch (transport){
case NETCONF_SOAP:
cprintf(xf, "</detail>" "</soapenv:Fault>");
default: /* fall through */
if (add_postamble(xf) < 0)
return -1;
break;
}
return 0;
}
/*! Look for a text pattern in an input string, one char at a time
* @param[in] tag What to look for
* @param[in] ch New input character
* @param[in,out] state A state integer holding how far we have parsed.
* @retval 0 No, we havent detected end tag
* @retval 1 Yes, we have detected end tag!
* XXX: move to clicon_xml?
*/
int
detect_endtag(char *tag, char ch, int *state)
{
int retval = 0;
if (tag[*state] == ch){
(*state)++;
if (*state == strlen(tag)){
*state = 0;
retval = 1;
}
}
else
*state = 0;
return retval;
}
/*
* target_locked
* return 1 if locked, 0 if not.
* return clientid if locked.
*/
int
target_locked(enum target_type target, int *client)
{
return 0;
}
/*
* unlock_target
*/
int
unlock_target(enum target_type target)
{
return 0;
}
int
lock_target(enum target_type target)
{
return 0;
}
/*! Get "target" attribute, return actual database given candidate or running
* Caller must do error handling
* @retval dbname Actual database file name
*/
char *
netconf_get_target(clicon_handle h, cxobj *xn, char *path)
{
cxobj *x;
char *target = NULL;
char *running_db;
char *candidate_db;
if ((running_db = clicon_running_db(h)) == NULL)
goto done;
if ((candidate_db = clicon_candidate_db(h)) == NULL)
goto done;
if ((x = xpath_first(xn, path)) != NULL){
if (xpath_first(x, "candidate") != NULL)
target = candidate_db;
else
if (xpath_first(x, "running") != NULL)
target = running_db;
}
done:
return target;
}
/*! Send netconf message from cbuf on socket
* @param[in] s
* @param[in] cb Cligen buffer that contains the XML message
* @param[in] msg Only for debug
*/
int
netconf_output(int s, cbuf *xf, char *msg)
{
char *buf = cbuf_get(xf);
int len = cbuf_len(xf);
int retval = -1;
clicon_debug(1, "SEND %s", msg);
if (debug > 1){ /* XXX: below only works to stderr, clicon_debug may log to syslog */
cxobj *xt = NULL;
if (clicon_xml_parse_string(&buf, &xt) == 0){
clicon_xml2file(stderr, xml_child_i(xt, 0), 0, 0);
fprintf(stderr, "\n");
xml_free(xt);
}
}
if (write(s, buf, len) < 0){
if (errno == EPIPE)
;
else
clicon_log(LOG_ERR, "%s: write: %s", __FUNCTION__, strerror(errno));
goto done;
}
retval = 0;
done:
return retval;
}

View file

@ -0,0 +1,75 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*
* Netconf lib
*****************************************************************************/
#ifndef _NETCONF_LIB_H_
#define _NETCONF_LIB_H_
/*
* Types
*/
enum target_type{ /* netconf */
RUNNING,
CANDIDATE
};
enum transport_type{
NETCONF_SSH, /* RFC 4742 */
NETCONF_SOAP, /* RFC 4743 */
};
enum test_option{ /* edit-config */
SET,
TEST_THEN_SET,
TEST_ONLY
};
enum error_option{ /* edit-config */
STOP_ON_ERROR,
CONTINUE_ON_ERROR
};
enum filter_option{ /* get-config/filter */
FILTER_SUBTREE,
FILTER_XPATH
};
/*
* Variables
*/
extern enum transport_type transport;
extern int cc_closed;
/*
* Prototypes
*/
void netconf_ok_set(int ok);
int netconf_ok_get(void);
int add_preamble(cbuf *xf);
int add_postamble(cbuf *xf);
int add_error_preamble(cbuf *xf, char *reason);
int detect_endtag(char *tag, char ch, int *state);
char *netconf_get_target(clicon_handle h, cxobj *xn, char *path);
int add_error_postamble(cbuf *xf);
int netconf_output(int s, cbuf *xf, char *msg);
#endif /* _NETCONF_LIB_H_ */

425
apps/netconf/netconf_main.c Normal file
View file

@ -0,0 +1,425 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <netinet/in.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clicon/clicon.h>
#include "clicon_netconf.h"
#include "netconf_lib.h"
#include "netconf_hello.h"
#include "netconf_plugin.h"
#include "netconf_rpc.h"
/* Command line options to be passed to getopt(3) */
#define NETCONF_OPTS "hDqf:d:S"
/*! Process incoming packet
* @param[in] h Clicon handle
* @param[in] xf Packet buffer
*/
static int
process_incoming_packet(clicon_handle h,
cbuf *xf)
{
char *str;
char *str0;
cxobj *xml_req = NULL; /* Request (in) */
int isrpc = 0; /* either hello or rpc */
cbuf *xf_out;
cbuf *xf_err;
cbuf *xf1;
clicon_debug(1, "RECV");
clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(xf));
if ((str0 = strdup(cbuf_get(xf))) == NULL){
clicon_log(LOG_ERR, "%s: strdup: %s", __FUNCTION__, strerror(errno));
return -1;
}
str = str0;
/* Parse incoming XML message */
if (clicon_xml_parse_string(&str, &xml_req) < 0){
if ((xf = cbuf_new()) == NULL){
netconf_create_rpc_error(xf, NULL,
"operation-failed",
"rpc", "error",
NULL,
NULL);
netconf_output(1, xf, "rpc-error");
cbuf_free(xf);
}
else
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
free(str0);
goto done;
}
free(str0);
if (xpath_first(xml_req, "//rpc") != NULL){
isrpc++;
}
else
if (xpath_first(xml_req, "//hello") != NULL)
;
else{
clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropp\
ed");
goto done;
}
/* Initialize response buffers */
if ((xf_out = cbuf_new()) == NULL){
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
goto done;
}
/* Create error buf */
if ((xf_err = cbuf_new()) == NULL){
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
goto done;
}
netconf_ok_set(0);
if (isrpc){
if (netconf_rpc_dispatch(h,
xml_req,
xpath_first(xml_req, "//rpc"),
xf_out, xf_err) < 0){
assert(cbuf_len(xf_err));
clicon_debug(1, "%s", cbuf_get(xf_err));
if (isrpc){
if (netconf_output(1, xf_err, "rpc-error") < 0)
goto done;
}
}
else{
if ((xf1 = cbuf_new()) != NULL){
if (netconf_create_rpc_reply(xf1, xml_req, cbuf_get(xf_out), netconf_ok_get()) < 0){
cbuf_free(xf_out);
cbuf_free(xf_err);
cbuf_free(xf1);
goto done;
}
if (netconf_output(1, xf1, "rpc-reply") < 0){
cbuf_reset(xf1);
netconf_create_rpc_error(xf1, xml_req, "operation-failed",
"protocol", "error",
NULL, cbuf_get(xf_err));
netconf_output(1, xf1, "rpc-error");
cbuf_free(xf_out);
cbuf_free(xf_err);
cbuf_free(xf1);
goto done;
}
cbuf_free(xf1);
}
}
}
else{
netconf_hello_dispatch(xml_req); /* XXX: return-value */
}
cbuf_free(xf_out);
cbuf_free(xf_err);
done:
if (xml_req)
xml_free(xml_req);
return 0;
}
/*! Get netconf message: detect end-of-msg
* @param[in] s Socket where input arrived. read from this.
* @param[in] arg Clicon handle.
*/
static int
netconf_input_cb(int s,
void *arg)
{
clicon_handle h = arg;
unsigned char buf[BUFSIZ];
int i;
int len;
static cbuf *xf; /* XXX: should use ce state? */
int xml_state = 0;
int retval = -1;
if (xf == NULL)
if ((xf = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "%s: cbuf_new", __FUNCTION__);
return retval;
}
memset(buf, 0, sizeof(buf));
if ((len = read(s, buf, sizeof(buf))) < 0){
if (errno == ECONNRESET)
len = 0; /* emulate EOF */
else{
clicon_log(LOG_ERR, "%s: read: %s", __FUNCTION__, strerror(errno));
goto done;
}
} /* read */
if (len == 0){ /* EOF */
cc_closed++;
close(s);
retval = 0;
goto done;
}
for (i=0; i<len; i++){
if (buf[i] == 0)
continue; /* Skip NULL chars (eg from terminals) */
cprintf(xf, "%c", buf[i]);
if (detect_endtag("]]>]]>",
buf[i],
&xml_state)) {
/* OK, we have an xml string from a client */
if (process_incoming_packet(h, xf) < 0){
goto done;
}
if (cc_closed)
break;
cbuf_reset(xf);
}
}
retval = 0;
done:
// cbuf_free(xf);
if (cc_closed)
retval = -1;
return retval;
}
/*
* send_hello
* args: s file descriptor to write on (eg 1 - stdout)
*/
static int
send_hello(int s)
{
cbuf *xf;
int retval = -1;
if ((xf = cbuf_new()) == NULL){
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
goto done;
}
if (netconf_create_hello(xf, getpid()) < 0)
goto done;
if (netconf_output(s, xf, "hello") < 0)
goto done;
retval = 0;
done:
if (xf)
cbuf_free(xf);
return retval;
}
/* from init_candidate_db() and clicon_rpc_copy() */
static int
init_candidate_db(clicon_handle h, char *running_db, char *candidate_db)
{
struct stat sb;
int retval = -1;
/* init shared candidate */
if (lstat(candidate_db, &sb) < 0){
if (clicon_rpc_copy(h, running_db, candidate_db) < 0)
goto done;
}
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
static int
terminate(clicon_handle h)
{
yang_spec *yspec;
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
clicon_handle_exit(h);
return 0;
}
/*
* usage
*/
static void
usage(char *argv0, clicon_handle h)
{
char *netconfdir = clicon_netconf_dir(h);
fprintf(stderr, "usage:%s\n"
"where options are\n"
"\t-h\t\tHelp\n"
"\t-D\t\tDebug\n"
"\t-q\t\tQuiet: dont send hello prompt\n"
"\t-f <file>\tConfiguration file (mandatory)\n"
"\t-d <dir>\tSpecify netconf plugin directory dir (default: %s)\n"
"\t-S\t\tLog on syslog\n",
argv0,
netconfdir
);
exit(0);
}
int
main(int argc, char **argv)
{
char c;
char *tmp;
char *argv0 = argv[0];
int quiet = 0;
clicon_handle h;
int use_syslog;
char *running_db;
char *candidate_db;
/* Defaults */
use_syslog = 0;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR);
/* Create handle */
if ((h = clicon_handle_init()) == NULL)
return -1;
while ((c = getopt(argc, argv, NETCONF_OPTS)) != -1)
switch (c) {
case 'h' : /* help */
usage(argv[0], h);
break;
case 'D' : /* debug */
debug = 1;
break;
case 'f': /* override config file */
if (!strlen(optarg))
usage(argv[0], h);
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
break;
case 'S': /* Log on syslog */
use_syslog = 1;
break;
}
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO,
use_syslog?CLICON_LOG_SYSLOG:CLICON_LOG_STDERR);
clicon_debug_init(debug, NULL);
/* Find and read configfile */
if (clicon_options_main(h) < 0)
return -1;
/* Now rest of options */
optind = 1;
opterr = 0;
while ((c = getopt(argc, argv, NETCONF_OPTS)) != -1)
switch (c) {
case 'h' : /* help */
case 'D' : /* debug */
case 'f': /* config file */
case 'S': /* Log on syslog */
break; /* see above */
case 'q': /* quiet: dont write hello */
quiet++;
break;
case 'd': /* Plugin directory */
if (!strlen(optarg))
usage(argv[0], h);
clicon_option_str_set(h, "CLICON_NETCONF_DIR", optarg);
break;
default:
usage(argv[0], h);
break;
}
argc -= optind;
argv += optind;
/* Parse db spec file */
if (yang_spec_main(h, stdout, 0) < 0)
goto done;
/* Initialize plugins group */
if (netconf_plugin_load(h) < 0)
return -1;
if ((running_db = clicon_running_db(h)) == NULL){
clicon_err(OE_FATAL, 0, "running db not set");
goto done;
}
if ((candidate_db = clicon_candidate_db(h)) == NULL){
clicon_err(OE_FATAL, 0, "candidate db not set");
goto done;
}
if (init_candidate_db(h, running_db, candidate_db) < 0)
return -1;
/* Call start function is all plugins before we go interactive */
tmp = *(argv-1);
*(argv-1) = argv0;
netconf_plugin_start(h, argc+1, argv-1);
*(argv-1) = tmp;
if (!quiet)
send_hello(1);
if (event_reg_fd(0, netconf_input_cb, h, "netconf socket") < 0)
goto done;
if (debug)
clicon_option_dump(h, debug);
if (event_loop() < 0)
goto done;
done:
netconf_plugin_unload(h);
terminate(h);
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
return 0;
}

View file

@ -0,0 +1,277 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*
* handling netconf plugins
*/
#ifdef HAVE_CONFIG_H
#include "clicon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#include <assert.h>
#include <dlfcn.h>
#include <dirent.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <netinet/in.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clicon/clicon.h>
/* clicon netconf*/
#include "clicon_netconf.h"
#include "netconf_lib.h"
#include "netconf_plugin.h"
/*
* Unload a plugin
*/
static int
plugin_unload(clicon_handle h, void *handle)
{
int retval = 0;
char *error;
plgexit_t *exitfn;
/* Call exit function is it exists */
exitfn = dlsym(handle, PLUGIN_EXIT);
if (dlerror() == NULL)
exitfn(h);
dlerror(); /* Clear any existing error */
if (dlclose(handle) != 0) {
error = (char*)dlerror();
clicon_err(OE_PLUGIN, errno, "dlclose: %s\n", error ? error : "Unknown error");
/* Just report */
}
return retval;
}
/*
* Load a dynamic plugin object and call it's init-function
* Note 'file' may be destructively modified
*/
static plghndl_t
plugin_load (clicon_handle h, char *file, int dlflags, const char *cnklbl)
{
char *error;
void *handle = NULL;
plginit_t *initfn;
dlerror(); /* Clear any existing error */
if ((handle = dlopen (file, dlflags)) == NULL) {
error = (char*)dlerror();
clicon_err(OE_PLUGIN, errno, "dlopen: %s\n", error ? error : "Unknown error");
goto quit;
}
/* call plugin_init() if defined */
if ((initfn = dlsym(handle, PLUGIN_INIT)) != NULL) {
if (initfn(h) != 0) {
clicon_err(OE_PLUGIN, errno, "Failed to initiate %s\n", strrchr(file,'/')?strchr(file, '/'):file);
goto quit;
}
}
quit:
return handle;
}
static int nplugins = 0;
static plghndl_t *plugins = NULL;
static netconf_reg_t *deps = NULL;
/*
* netconf_plugin_load
* Load allplugins you can find in CLICON_NETCONF_DIR
*/
int
netconf_plugin_load(clicon_handle h)
{
int retval = -1;
char *dir;
int ndp;
struct dirent *dp;
int i;
char *filename;
plghndl_t *handle;
if ((dir = clicon_netconf_dir(h)) == NULL){
clicon_err(OE_PLUGIN, 0, "clicon_netconf_dir not defined");
goto quit;
}
/* Get plugin objects names from plugin directory */
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG, __FUNCTION__))<0)
goto quit;
/* Load all plugins */
for (i = 0; i < ndp; i++) {
filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dp[i].d_name);
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
(int)strlen(filename), filename);
if (filename == NULL) {
clicon_err(OE_UNIX, errno, "chunk");
goto quit;
}
if ((handle = plugin_load (h, filename, RTLD_NOW, __FUNCTION__)) == NULL)
goto quit;
if ((plugins = rechunk(plugins, (nplugins+1) * sizeof (*plugins), NULL)) == NULL) {
clicon_err(OE_UNIX, errno, "chunk");
goto quit;
}
plugins[nplugins++] = handle;
unchunk (filename);
}
retval = 0;
quit:
unchunk_group(__FUNCTION__);
return retval;
}
int
netconf_plugin_unload(clicon_handle h)
{
int i;
netconf_reg_t *nr;
while((nr = deps) != NULL) {
DELQ(nr, deps, netconf_reg_t *);
if (nr->nr_tag)
free(nr->nr_tag);
free(nr);
}
for (i = 0; i < nplugins; i++)
plugin_unload(h, plugins[i]);
if (plugins)
unchunk(plugins);
nplugins = 0;
return 0;
}
/*
* Call plugin_start in all plugins
*/
int
netconf_plugin_start(clicon_handle h, int argc, char **argv)
{
int i;
plgstart_t *startfn;
for (i = 0; i < nplugins; i++) {
/* Call exit function is it exists */
if ((startfn = dlsym(plugins[i], PLUGIN_START)) == NULL)
break;
optind = 0;
if (startfn(h, argc, argv) < 0) {
clicon_debug(1, "plugin_start() failed\n");
return -1;
}
}
return 0;
}
/*
* netconf_register_callback
* Called from plugin to register a callback for a specific netconf XML tag.
*/
int
netconf_register_callback(clicon_handle h,
netconf_cb_t cb, /* Callback called */
void *arg, /* Arg to send to callback */
char *tag) /* Xml tag when callback is made */
{
netconf_reg_t *nr;
if ((nr = malloc(sizeof(netconf_reg_t))) == NULL) {
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
goto catch;
}
memset (nr, 0, sizeof (*nr));
nr->nr_callback = cb;
nr->nr_arg = arg;
nr->nr_tag = strdup(tag); /* strdup */
INSQ(nr, deps);
return 0;
catch:
if (nr){
if (nr->nr_tag)
free(nr->nr_tag);
free(nr);
}
return -1;
}
/*! See if there is any callback registered for this tag
*
* @param xn Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
* @param xf Output xml stream. For reply
* @param xf_err Error xml stream. For error reply
* @param xorig Original request.
*
* @retval -1 Error
* @retval 0 OK, not found handler.
* @retval 1 OK, handler called
*/
int
netconf_plugin_callbacks(clicon_handle h,
cxobj *xn,
cbuf *xf,
cbuf *xf_err,
cxobj *xorig)
{
netconf_reg_t *nr;
int retval;
if (deps == NULL)
return 0;
nr = deps;
do {
if (strcmp(nr->nr_tag, xml_name(xn)) == 0){
if ((retval = nr->nr_callback(h,
xorig,
xn,
xf,
xf_err,
nr->nr_arg)) < 0)
return -1;
else
return 1; /* handled */
}
nr = NEXTQ(netconf_reg_t *, nr);
} while (nr != deps);
return 0;
}

View file

@ -0,0 +1,57 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*
* handling netconf plugins
*****************************************************************************/
#ifndef _NETCONF_PLUGIN_H_
#define _NETCONF_PLUGIN_H_
/*
* Types
*/
/* Database dependency description */
struct netconf_reg {
qelem_t nr_qelem; /* List header */
netconf_cb_t nr_callback; /* Validation/Commit Callback */
void *nr_arg; /* Application specific argument to cb */
char *nr_tag; /* Xml tag when matched, callback called */
};
typedef struct netconf_reg netconf_reg_t;
/*
* Prototypes
*/
int netconf_plugin_load(clicon_handle h);
int netconf_plugin_start(clicon_handle h, int argc, char **argv);
int netconf_plugin_unload(clicon_handle h);
int netconf_plugin_callbacks(clicon_handle h,
// dbspec_key *dbspec,
cxobj *xn,
cbuf *xf,
cbuf *xf_err,
cxobj *xt);
#endif /* _NETCONF_PLUGIN_H_ */

1258
apps/netconf/netconf_rpc.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
/*
*
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
This file is part of CLICON.
CLICON is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
CLICON is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CLICON; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>.
*
* Code for handling netconf rpc messages
*****************************************************************************/
#ifndef _NETCONF_RPC_H_
#define _NETCONF_RPC_H_
/*
* Prototypes
*/
int
netconf_rpc_dispatch(clicon_handle h,
cxobj *xorig,
cxobj *xn,
cbuf *xf,
cbuf *xf_err);
int netconf_create_rpc_reply(cbuf *xf, /* msg buffer */
cxobj *xr, /* orig request */
char *body, int ok);
int netconf_create_rpc_error(cbuf *xf, /* msg buffer */
cxobj *xr, /* orig request */
char *tag,
char *type,
char *severity,
char *message,
char *info);
#endif /* _NETCONF_RPC_H_ */