internal netconf hello mechanism to obtain session-id

This commit is contained in:
Olof hagsand 2019-10-24 11:46:06 +02:00
parent 59825214f3
commit 44138c0071
14 changed files with 250 additions and 264 deletions

View file

@ -54,7 +54,9 @@
### Minor changes ### Minor changes
* Changed session-id handing. Instead of using pid of peer process, a proper session id generated by the server is used, following RFC6241. * Changed session-id handing. Instead of using pid of peer process, a proper session id generated by the server is used, following RFC6241.
* Code in kill_session is removed where this fact was used to actually kill the client process. * Clients query with a hello to the server at startup and expects a hello reply
back containing the session-id. Which is then used in further communication.
* Code in kill_session is removed where this fact was used to actually kill the client process. Now only the session endpoint is closed.
* XPATH canonical form implemented for NETCONF get and get-config. This means that all callbacks (including state callbacks) will have the prefixes that corresponds to module prefixes. If an xpath have other prefixes (or null as default), they will be translated to canonical form before any callbacks. * XPATH canonical form implemented for NETCONF get and get-config. This means that all callbacks (including state callbacks) will have the prefixes that corresponds to module prefixes. If an xpath have other prefixes (or null as default), they will be translated to canonical form before any callbacks.
* Example of a canonical form: `/a:x/a:y`, then symbols must belong to a yang module with prefix `a`. * Example of a canonical form: `/a:x/a:y`, then symbols must belong to a yang module with prefix `a`.
* FreeBSD modifications: Configure, makefiles and test scripts modification for Freebsd * FreeBSD modifications: Configure, makefiles and test scripts modification for Freebsd

View file

@ -158,7 +158,7 @@ backend_client_rm(clicon_handle h,
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[in] xpath Xpath selection, not used but may be to filter early * @param[in] xpath Xpath selection, not used but may be to filter early
* @param[out] xrs XML restconf-state node * @param[out] xrs XML restconf-state node
* @see netconf_create_hello * @see netconf_hello_server
* @see rfc8040 Sections 9.1 * @see rfc8040 Sections 9.1
*/ */
static int static int
@ -1300,6 +1300,29 @@ verify_nacm_user(enum nacm_credentials_t mode,
goto done; goto done;
} }
/*!
* @retval 0 OK
* @retval -1 Error
*/
static int
from_client_hello(clicon_handle h,
cxobj *x,
struct client_entry *ce,
cbuf *cbret)
{
int retval = -1;
uint32_t id;
id = clicon_session_id_get(h);
id++;
clicon_session_id_set(h, id);
cprintf(cbret, "<hello><session-id>%lu</session-id></hello>", id);
retval = 0;
// done:
return retval;
}
/*! An internal clicon message has arrived from a client. Receive and dispatch. /*! An internal clicon message has arrived from a client. Receive and dispatch.
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] s Socket where message arrived. read from this. * @param[in] s Socket where message arrived. read from this.
@ -1344,12 +1367,20 @@ from_client_msg(clicon_handle h,
goto done; goto done;
goto reply; goto reply;
} }
ce->ce_id = id;
if ((x = xpath_first_nsc(xt, NULL, "/rpc")) == NULL){ if ((x = xpath_first_nsc(xt, NULL, "/rpc")) == NULL){
if (netconf_malformed_message(cbret, "rpc keyword expected")< 0) if ((x = xpath_first_nsc(xt, NULL, "/hello")) != NULL){
goto done; if ((ret = from_client_hello(h, x, ce, cbret)) <0)
goto reply; goto done;
goto reply;
}
else{
if (netconf_malformed_message(cbret, "rpc keyword expected")< 0)
goto done;
goto reply;
}
} }
ce->ce_id = id;
/* Populate incoming XML tree with yang - /* Populate incoming XML tree with yang -
* should really have been dealt with by decode above * should really have been dealt with by decode above
* maybe not necessary since it should be */ * maybe not necessary since it should be */

View file

@ -285,6 +285,7 @@ main(int argc, char **argv)
char *str; char *str;
int tabmode; int tabmode;
char *dir; char *dir;
uint32_t id = 0;
/* Defaults */ /* Defaults */
once = 0; once = 0;
@ -595,14 +596,15 @@ main(int argc, char **argv)
if (result < 0) if (result < 0)
goto done; goto done;
} }
#if 1
/* XXX get session id from backend hello */
clicon_session_id_set(h, getpid());
#endif
/* Go into event-loop unless -1 command-line */ /* Go into event-loop unless -1 command-line */
if (!once) if (!once){
/* send hello request from backend hello */
if (clicon_hello_req(h, &id) < 0)
goto done;
clicon_session_id_set(h, id);
retval = cli_interactive(h); retval = cli_interactive(h);
}
else else
retval = 0; retval = 0;
done: done:

View file

@ -70,7 +70,6 @@ APPL = clixon_netconf
# Not accessible from plugin # Not accessible from plugin
APPSRC = netconf_main.c APPSRC = netconf_main.c
APPSRC += netconf_hello.c
APPSRC += netconf_rpc.c APPSRC += netconf_rpc.c
APPSRC += netconf_filter.c APPSRC += netconf_filter.c
APPOBJ = $(APPSRC:.c=.o) APPOBJ = $(APPSRC:.c=.o)

View file

@ -1,188 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
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 *****
*
* 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 "clixon_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 <clixon/clixon.h>
#include "netconf_lib.h"
#include "netconf_hello.h"
static int
netconf_hello(cxobj *xn)
{
#ifdef nyi
cxobj *x;
x = NULL;
while ((x = xpath_each(xn, "//capability", x)) != NULL) {
}
#endif
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;
}
/*! Create Netconf hello. Single cap and defer individual to querying modules
* @param[in] h Clicon handle
* @param[in] cb Msg buffer
* @param[in] session_id Id of client session
* Lots of dependencies here. regarding the hello protocol.
* RFC6241 NETCONF Protocol says: (8.1)
* MUST send a <hello> element containing a list of that peer's capabilities
* MUST send at least the base NETCONF capability, urn:ietf:params:netconf:base:1.1
* MAY include capabilities for previous NETCONF versions
* A server MUST include a <session-id>
* A client MUST NOT include a <session-id>
* A server receiving <session-id> MUST terminate the NETCONF session.
* A client not receiving <session-id> MUST terminate w/o sending<close-session>
* the example shows urn:ietf:params:netconf:capability:startup:1.0
* RFC5277 NETCONF Event Notifications
* urn:ietf:params:netconf:capability:notification:1.0 is advertised during the capability exchange
*
* RFC6022 YANG Module for NETCONF Monitoring
* MUST advertise the capability URI "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
* RFC7895 Yang module library defines how to announce module features (not hell capabilities)
* RFC7950 YANG 1.1 says (5.6.4);
* MUST announce the modules it implements by implementing the YANG module
* "ietf-yang-library" (RFC7895) and listing all implemented modules in the
* "/modules-state/module" list.
* MUST advertise urn:ietf:params:netconf:capability:yang-library:1.0?
* revision=<date>&module-set-id=<id> in the <hello> message.
*
* Question: should the NETCONF in RFC6241 sections 8.2-8.9 be announced both
* as features and as capabilities in the <hello> message according to RFC6241?
* urn:ietf:params:netconf:capability:candidate:1.0 (8.3)
* urn:ietf:params:netconf:capability:validate:1.1 (8.6)
* urn:ietf:params:netconf:capability:startup:1.0 (8.7)
* urn:ietf:params:netconf:capability:xpath:1.0 (8.9)
* urn:ietf:params:netconf:capability:notification:1.0 (RFC5277)
*
* @note the hello message is created bythe netconf application, not the
* backend, and backend may implement more modules - please consider if using
* library routines for detecting capabilities here. In contrast, yang module
* list (RFC7895) is processed by the backend.
* @note encode bodies, see xml_chardata_encode()
* @see yang_modules_state_get
* @see netconf_module_load
*/
int
netconf_create_hello(clicon_handle h,
cbuf *cb,
int session_id)
{
int retval = -1;
char *module_set_id;
char *ietf_yang_library_revision;
char *encstr = NULL;
module_set_id = clicon_option_str(h, "CLICON_MODULE_SET_ID");
if ((ietf_yang_library_revision = yang_modules_revision(h)) == NULL)
goto done;
add_preamble(cb);
cprintf(cb, "<hello xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
cprintf(cb, "<capabilities>");
cprintf(cb, "<capability>urn:ietf:params:netconf:base:1.0</capability>");
if (xml_chardata_encode(&encstr, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=%s&module-set-id=%s",
ietf_yang_library_revision,
module_set_id) < 0)
goto done;
cprintf(cb, "<capability>%s</capability>", encstr);
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>");
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:validate:1.1</capability>");
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:startup:1.0</capability>");
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:xpath:1.0</capability>");
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:notification:1.0</capability>");
cprintf(cb, "</capabilities>");
cprintf(cb, "<session-id>%lu</session-id>", (long unsigned int)session_id);
cprintf(cb, "</hello>");
add_postamble(cb);
retval = 0;
done:
if (encstr)
free(encstr);
return retval;
}

View file

@ -1,47 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
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 *****
*
* Code for handling netconf hello messages
*****************************************************************************/
#ifndef _NETCONF_HELLO_H_
#define _NETCONF_HELLO_H_
/*
* Prototypes
*/
int netconf_create_hello(clicon_handle h, cbuf *cb, int session_id);
int netconf_hello_dispatch(cxobj *xn);
#endif /* _NETCONF_HELLO_H_ */

View file

@ -67,7 +67,6 @@
#include "clixon_netconf.h" #include "clixon_netconf.h"
#include "netconf_lib.h" #include "netconf_lib.h"
#include "netconf_hello.h"
#include "netconf_rpc.h" #include "netconf_rpc.h"
/* Command line options to be passed to getopt(3) */ /* Command line options to be passed to getopt(3) */
@ -78,6 +77,33 @@
/*! Ignore errors on packet errors: continue */ /*! Ignore errors on packet errors: continue */
static int ignore_packet_errors = 1; static int ignore_packet_errors = 1;
static int
netconf_hello(cxobj *xn)
{
#ifdef nyi
cxobj *x;
x = NULL;
while ((x = xpath_each(xn, "//capability", x)) != NULL) {
}
#endif
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;
}
/*! Process incoming packet /*! Process incoming packet
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] cb Packet buffer * @param[in] cb Packet buffer
@ -268,7 +294,8 @@ netconf_input_cb(int s,
*/ */
static int static int
send_hello(clicon_handle h, send_hello(clicon_handle h,
int s) int s,
uint32_t id)
{ {
int retval = -1; int retval = -1;
cbuf *cb; cbuf *cb;
@ -277,7 +304,7 @@ send_hello(clicon_handle h,
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__); clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
goto done; goto done;
} }
if (netconf_create_hello(h, cb, getpid()) < 0) if (netconf_hello_server(h, cb, id) < 0)
goto done; goto done;
if (netconf_output(s, cb, "hello") < 0) if (netconf_output(s, cb, "hello") < 0)
goto done; goto done;
@ -334,16 +361,16 @@ usage(clicon_handle h,
"\t-h\t\tHelp\n" "\t-h\t\tHelp\n"
"\t-D <level>\tDebug level\n" "\t-D <level>\tDebug level\n"
"\t-f <file>\tConfiguration file (mandatory)\n" "\t-f <file>\tConfiguration file (mandatory)\n"
"\t-l (e|o|s|f<file>) \tLog on std(e)rr, std(o)ut, (s)yslog, (f)ile (syslog is default)\n" "\t-l (e|o|s|f<file>) Log on std(e)rr, std(o)ut, (s)yslog(default), (f)ile\n"
"\t-q\t\tQuiet: dont send hello prompt\n" "\t-q\t\tQuiet: dont send hello prompt\n"
"\t-a UNIX|IPv4|IPv6\tInternal backend socket family\n" "\t-a UNIX|IPv4|IPv6 Internal backend socket family\n"
"\t-u <path|addr>\tInternal socket domain path or IP addr (see -a)\n" "\t-u <path|addr>\tInternal socket domain path or IP addr (see -a)\n"
"\t-d <dir>\tSpecify netconf plugin directory dir (default: %s)\n" "\t-d <dir>\tSpecify netconf plugin directory dir (default: %s)\n"
"\t-p <dir>\tYang directory path (see CLICON_YANG_DIR)\n" "\t-p <dir>\tYang directory path (see CLICON_YANG_DIR)\n"
"\t-y <file>\tLoad yang spec file (override yang main module)\n" "\t-y <file>\tLoad yang spec file (override yang main module)\n"
"\t-U <user>\tOver-ride unix user with a pseudo user for NACM.\n" "\t-U <user>\tOver-ride unix user with a pseudo user for NACM.\n"
"\t-t <sec>\tTimeout in seconds. Quit after this time.\n" "\t-t <sec>\tTimeout in seconds. Quit after this time.\n"
"\t-e \tDont ignore errors on packet input.\n" "\t-e \t\tDont ignore errors on packet input.\n"
"\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n", "\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0, argv0,
clicon_netconf_dir(h) clicon_netconf_dir(h)
@ -367,6 +394,7 @@ main(int argc,
yang_stmt *yspec = NULL; yang_stmt *yspec = NULL;
yang_stmt *yspecfg = NULL; /* For config XXX clixon bug */ yang_stmt *yspecfg = NULL; /* For config XXX clixon bug */
char *str; char *str;
uint32_t id;
/* Create handle */ /* Create handle */
if ((h = clicon_handle_init()) == NULL) if ((h = clicon_handle_init()) == NULL)
@ -534,8 +562,13 @@ main(int argc,
clicon_session_id_set(h, getpid()); clicon_session_id_set(h, getpid());
#endif #endif
/* send hello request from backend hello */
if (clicon_hello_req(h, &id) < 0)
goto done;
clicon_session_id_set(h, id);
if (!quiet) if (!quiet)
send_hello(h, 1); send_hello(h, 1, id);
if (event_reg_fd(0, netconf_input_cb, h, "netconf socket") < 0) if (event_reg_fd(0, netconf_input_cb, h, "netconf socket") < 0)
goto done; goto done;
if (debug) if (debug)

View file

@ -555,9 +555,9 @@ usage(clicon_handle h,
"\t-p <dir>\tYang directory path (see CLICON_YANG_DIR)\n" "\t-p <dir>\tYang directory path (see CLICON_YANG_DIR)\n"
"\t-d <dir>\tSpecify restconf plugin directory dir (default: %s)\n" "\t-d <dir>\tSpecify restconf plugin directory dir (default: %s)\n"
"\t-y <file>\tLoad yang spec file (override yang main module)\n" "\t-y <file>\tLoad yang spec file (override yang main module)\n"
"\t-a UNIX|IPv4|IPv6\tInternal backend socket family\n" "\t-a UNIX|IPv4|IPv6 Internal backend socket family\n"
"\t-u <path|addr>\tInternal socket domain path or IP addr (see -a)\n" "\t-u <path|addr>\tInternal socket domain path or IP addr (see -a)\n"
"\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n", "\t-o \"<option>=<value>\" Give configuration option overriding config file (see clixon-config.yang)\n",
argv0, argv0,
clicon_restconf_dir(h) clicon_restconf_dir(h)
); );
@ -587,6 +587,7 @@ main(int argc,
int finish; int finish;
char *str; char *str;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
uint32_t id = 0;
/* In the startup, logs to stderr & debug flag set later */ /* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst); clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@ -780,10 +781,11 @@ main(int argc,
clicon_err(OE_CFG, errno, "FCGX_OpenSocket"); clicon_err(OE_CFG, errno, "FCGX_OpenSocket");
goto done; goto done;
} }
#if 1 /* send hello request from backend hello */
/* XXX get session id from backend hello */ if (clicon_hello_req(h, &id) < 0)
clicon_session_id_set(h, getpid()); goto done;
#endif clicon_session_id_set(h, id);
if (clicon_socket_set(h, sock) < 0) if (clicon_socket_set(h, sock) < 0)
goto done; goto done;
/* umask settings may interfer: we want group to write: this is 774 */ /* umask settings may interfer: we want group to write: this is 774 */

4
configure vendored
View file

@ -4928,8 +4928,8 @@ if ac_fn_c_try_compile "$LINENO"; then :
$as_echo "#define HAVE_SO_PEERCRED 1" >>confdefs.h $as_echo "#define HAVE_SO_PEERCRED 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Have getsockopt peercred" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: result: Have getsockopt SO_PEERCRED" >&5
$as_echo "Have getsockopt peercred" >&6; } $as_echo "Have getsockopt SO_PEERCRED" >&6; }
fi fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext

View file

@ -93,5 +93,7 @@ char *netconf_db_find(cxobj *xn, char *name);
int netconf_err2cb(cxobj *xerr, cbuf **cberr); int netconf_err2cb(cxobj *xerr, cbuf **cberr);
const netconf_content netconf_content_str2int(char *str); const netconf_content netconf_content_str2int(char *str);
const char *netconf_content_int2str(netconf_content nr); const char *netconf_content_int2str(netconf_content nr);
int netconf_hello_server(clicon_handle h, cbuf *cb, uint32_t session_id);
int netconf_hello_req(clicon_handle h, cbuf *cb);
#endif /* _CLIXON_NETCONF_LIB_H */ #endif /* _CLIXON_NETCONF_LIB_H */

View file

@ -61,5 +61,6 @@ int clicon_rpc_discard_changes(clicon_handle h);
int clicon_rpc_create_subscription(clicon_handle h, char *stream, char *filter, int clicon_rpc_create_subscription(clicon_handle h, char *stream, char *filter,
int *s); int *s);
int clicon_rpc_debug(clicon_handle h, int level); int clicon_rpc_debug(clicon_handle h, int level);
int clicon_hello_req(clicon_handle h, uint32_t *id);
#endif /* _CLIXON_PROTO_CLIENT_H_ */ #endif /* _CLIXON_PROTO_CLIENT_H_ */

View file

@ -67,6 +67,7 @@
#include "clixon_xml_map.h" #include "clixon_xml_map.h"
#include "clixon_xpath_ctx.h" #include "clixon_xpath_ctx.h"
#include "clixon_xpath.h" #include "clixon_xpath.h"
#include "clixon_yang_module.h"
#include "clixon_netconf_lib.h" #include "clixon_netconf_lib.h"
/*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A /*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A
@ -1397,3 +1398,102 @@ netconf_content_int2str(netconf_content nr)
{ {
return clicon_int2str(netconf_content_map, nr); return clicon_int2str(netconf_content_map, nr);
} }
/*! Create Netconf server hello. Single cap and defer individual to querying modules
* @param[in] h Clicon handle
* @param[in] cb Msg buffer
* @param[in] session_id Id of client session
* Lots of dependencies here. regarding the hello protocol.
* RFC6241 NETCONF Protocol says: (8.1)
* MUST send a <hello> element containing a list of that peer's capabilities
* MUST send at least the base NETCONF capability, urn:ietf:params:netconf:base:1.1
* MAY include capabilities for previous NETCONF versions
* A server MUST include a <session-id>
* A client MUST NOT include a <session-id>
* A server receiving <session-id> MUST terminate the NETCONF session.
* A client not receiving <session-id> MUST terminate w/o sending<close-session>
* the example shows urn:ietf:params:netconf:capability:startup:1.0
* RFC5277 NETCONF Event Notifications
* urn:ietf:params:netconf:capability:notification:1.0 is advertised during the capability exchange
*
* RFC6022 YANG Module for NETCONF Monitoring
* MUST advertise the capability URI "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
* RFC7895 Yang module library defines how to announce module features (not hell capabilities)
* RFC7950 YANG 1.1 says (5.6.4);
* MUST announce the modules it implements by implementing the YANG module
* "ietf-yang-library" (RFC7895) and listing all implemented modules in the
* "/modules-state/module" list.
* MUST advertise urn:ietf:params:netconf:capability:yang-library:1.0?
* revision=<date>&module-set-id=<id> in the <hello> message.
*
* Question: should the NETCONF in RFC6241 sections 8.2-8.9 be announced both
* as features and as capabilities in the <hello> message according to RFC6241?
* urn:ietf:params:netconf:capability:candidate:1.0 (8.3)
* urn:ietf:params:netconf:capability:validate:1.1 (8.6)
* urn:ietf:params:netconf:capability:startup:1.0 (8.7)
* urn:ietf:params:netconf:capability:xpath:1.0 (8.9)
* urn:ietf:params:netconf:capability:notification:1.0 (RFC5277)
*
* @note the hello message is created bythe netconf application, not the
* backend, and backend may implement more modules - please consider if using
* library routines for detecting capabilities here. In contrast, yang module
* list (RFC7895) is processed by the backend.
* @note encode bodies, see xml_chardata_encode()
* @see yang_modules_state_get
* @see netconf_module_load
*/
int
netconf_hello_server(clicon_handle h,
cbuf *cb,
uint32_t session_id)
{
int retval = -1;
char *module_set_id;
char *ietf_yang_library_revision;
char *encstr = NULL;
module_set_id = clicon_option_str(h, "CLICON_MODULE_SET_ID");
if ((ietf_yang_library_revision = yang_modules_revision(h)) == NULL)
goto done;
cprintf(cb, "<hello xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
cprintf(cb, "<capabilities>");
cprintf(cb, "<capability>urn:ietf:params:netconf:base:1.0</capability>");
if (xml_chardata_encode(&encstr, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=%s&module-set-id=%s",
ietf_yang_library_revision,
module_set_id) < 0)
goto done;
cprintf(cb, "<capability>%s</capability>", encstr);
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>");
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:validate:1.1</capability>");
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:startup:1.0</capability>");
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:xpath:1.0</capability>");
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:notification:1.0</capability>");
cprintf(cb, "</capabilities>");
if (session_id)
cprintf(cb, "<session-id>%lu</session-id>", (long unsigned int)session_id);
cprintf(cb, "</hello>");
cprintf(cb, "]]>]]>");
retval = 0;
done:
if (encstr)
free(encstr);
return retval;
}
int
netconf_hello_req(clicon_handle h,
cbuf *cb)
{
int retval = -1;
cprintf(cb, "<hello xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
cprintf(cb, "<capabilities>");
cprintf(cb, "<capability>urn:ietf:params:netconf:base:1.0</capability>");
cprintf(cb, "</capabilities>");
cprintf(cb, "</hello>");
cprintf(cb, "]]>]]>");
retval = 0;
return retval;
}

View file

@ -902,7 +902,6 @@ clicon_rpc_debug(clicon_handle h,
char *username; char *username;
username = clicon_username_get(h); username = clicon_username_get(h);
/* XXX: hardcoded example yang, should be clixon-config!!! */
if ((msg = clicon_msg_encode(clicon_session_id_get(h), if ((msg = clicon_msg_encode(clicon_session_id_get(h),
"<rpc username=\"%s\"><debug xmlns=\"http://clicon.org/lib\"><level>%d</level></debug></rpc>", username?username:"", level)) == NULL) "<rpc username=\"%s\"><debug xmlns=\"http://clicon.org/lib\"><level>%d</level></debug></rpc>", username?username:"", level)) == NULL)
goto done; goto done;
@ -925,3 +924,53 @@ clicon_rpc_debug(clicon_handle h,
return retval; return retval;
} }
/*! Send a debug request to backend server
* @param[in] h CLICON handle
* @param[in] level Debug level
* @retval 0 OK
* @retval -1 Error and logged to syslog
*/
int
clicon_hello_req(clicon_handle h,
uint32_t *id)
{
int retval = -1;
struct clicon_msg *msg = NULL;
cxobj *xret = NULL;
cxobj *xerr;
cxobj *x;
char *username;
char *b;
int ret;
username = clicon_username_get(h);
if ((msg = clicon_msg_encode(0, "<hello username=\"%s\" xmlns=\"%s\"><capabilities><capability>urn:ietf:params:netconf:base:1.0</capability></capabilities></hello>",
username?username:"",
NETCONF_BASE_NAMESPACE)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Hello", xerr);
goto done;
}
if ((x = xpath_first(xret, "hello/session-id")) == NULL){
clicon_err(OE_XML, 0, "hello session-id");
goto done;
}
b = xml_body(x);
if ((ret = parse_uint32(b, id, NULL)) <= 0){
clicon_err(OE_XML, errno, "parse_uint32");
goto done;
}
fprintf(stderr, "id:%lu\n", *id);
retval = 0;
done:
if (msg)
free(msg);
if (xret)
xml_free(xret);
return retval;
}

View file

@ -278,7 +278,7 @@ x +--ro namespace inet:uri
+--ro name yang:yang-identifier +--ro name yang:yang-identifier
+--ro revision union +--ro revision union
+--ro schema? inet:uri +--ro schema? inet:uri
* @see netconf_create_hello * @see netconf_hello_server
*/ */
int int
yang_modules_state_get(clicon_handle h, yang_modules_state_get(clicon_handle h,