* Fixed: [CLIXON is not waiting for the hello message #184](https://github.com/clicon/clixon/issues/184)

* Hello message semantics has been made stricter according to RFC 6241 Sec 8.1, for example:
  * A client MUST send a <hello> element.
  * Each peer MUST send at least the base NETCONF capability, "urn:ietf:params:netconf:base:1.1" (or 1.0 for RFC 4741)
  * The netconf client will terminate (close the socket) if the client does not comply
  * You can set `CLICON_NETCONF_HELLO_OPTIONAL` to true to use the old behavior of essentially ignoring hellos.
* New clixon-config@2020-03-08.yang revision
  * Added: `CLICON_NETCONF_HELLO_OPTIONAL`
* The base capability has been changed to "urn:ietf:params:netconf:base:1.1" following RFC6241.
This commit is contained in:
Olof hagsand 2021-03-10 13:56:53 +01:00
parent 608f298ed9
commit 5692072d36
81 changed files with 1189 additions and 1351 deletions

View file

@ -195,6 +195,40 @@ clixon_client_lock(int sock,
return retval;
}
/*! Internal function to construct the encoding and hello message
*
* @param[in] sock Socket
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
* @param[in] xpath XPath
* @param[out] xdata XML data tree (may or may not include the intended data)
* @retval 0 OK
* @retval -1 Error
*/
static int
clixon_client_hello(int sock)
{
int retval = -1;
cbuf *msg = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if ((msg = cbuf_new()) == NULL){
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
cprintf(msg, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
cprintf(msg, "<hello xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
cprintf(msg, "<capabilities><capability>%s</capability></capabilities>", NETCONF_BASE_CAPABILITY_1_1);
cprintf(msg, "</hello>");
if (clicon_msg_send1(sock, msg) < 0)
goto done;
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (msg)
cbuf_free(msg);
return retval;
}
/*! Connect client to clixon backend according to config and return a socket
* @param[in] h Clixon handle
* @param[in] socktype Type of socket, internal/external/netconf/ssh
@ -260,6 +294,9 @@ clixon_client_connect(clicon_handle h,
if (clixon_proc_socket(argv, &cch->cch_pid, &cch->cch_socket) < 0){
goto err;
}
/* Start with encoding and hello message */
if (clixon_client_hello(cch->cch_socket) < 0)
goto err;
break;
case CLIXON_CLIENT_SSH:
break;
@ -349,6 +386,7 @@ clixon_xml_bottom(cxobj *xtop,
return retval;
}
/*! Internal function to construct a get-config and query a value from the backend
*
* @param[in] sock Socket

View file

@ -1541,7 +1541,13 @@ netconf_hello_server(clicon_handle h,
cprintf(cb, "<hello xmlns=\"%s\" message-id=\"%u\">", NETCONF_BASE_NAMESPACE, 42);
cprintf(cb, "<capabilities>");
cprintf(cb, "<capability>urn:ietf:params:netconf:base:1.0</capability>");
/* Each peer MUST send at least the base NETCONF capability, "urn:ietf:params:netconf:base:1.1"
* RFC 6241 Sec 8.1
*/
cprintf(cb, "<capability>%s</capability>", NETCONF_BASE_CAPABILITY_1_1);
/* A peer MAY include capabilities for previous NETCONF versions, to indicate
that it supports multiple protocol versions. */
cprintf(cb, "<capability>%s</capability>", NETCONF_BASE_CAPABILITY_1_0);
/* Check if RFC7895 loaded and revision found */
if ((ietf_yang_library_revision = yang_modules_revision(h)) != NULL){
if (xml_chardata_encode(&encstr, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=%s&module-set-id=%s",
@ -1575,7 +1581,7 @@ netconf_hello_req(clicon_handle h,
cprintf(cb, "<hello xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
cprintf(cb, "<capabilities>");
cprintf(cb, "<capability>urn:ietf:params:netconf:base:1.0</capability>");
cprintf(cb, "<capability>%s</capability>", NETCONF_BASE_CAPABILITY_1_1);
cprintf(cb, "</capabilities>");
cprintf(cb, "</hello>");
cprintf(cb, "]]>]]>");

View file

@ -1187,9 +1187,10 @@ clicon_hello_req(clicon_handle h,
int ret;
username = clicon_username_get(h);
if ((msg = clicon_msg_encode(0, "<hello username=\"%s\" xmlns=\"%s\" message-id=\"42\"><capabilities><capability>urn:ietf:params:netconf:base:1.0</capability></capabilities></hello>",
if ((msg = clicon_msg_encode(0, "<hello username=\"%s\" xmlns=\"%s\" message-id=\"42\"><capabilities><capability>%s</capability></capabilities></hello>",
username?username:"",
NETCONF_BASE_NAMESPACE)) == NULL)
NETCONF_BASE_NAMESPACE,
NETCONF_BASE_CAPABILITY_1_1)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret) < 0)
goto done;

View file

@ -543,8 +543,38 @@ xml_bind_yang_rpc(cxobj *xrpc,
cxobj *xc;
opname = xml_name(xrpc);
if ((strcmp(opname, "hello")) == 0) /* Hello: dont bind, dont appear in any yang spec */
if ((strcmp(opname, "hello")) == 0){
/* Hello: dont bind, dont appear in any yang spec, just ensure there is nothing apart from
* session-id or capabilities/capability tags
* If erro, just log, drop and close, rpc-error should not be sent since it is not rpc
*/
x = NULL;
while ((x = xml_child_each(xrpc, x, CX_ELMNT)) != NULL) {
name = xml_name(x);
if (strcmp(name, "session-id") == 0)
continue;
else if (strcmp(name, "capabilities") == 0){
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(xc), "capability") != 0){
if (xerr &&
netconf_unknown_element_xml(xerr, "protocol", xml_name(xc),
"Unrecognized hello/capabilities element") < 0)
goto done;
goto fail;
}
}
}
else {
if (xerr &&
netconf_unknown_element_xml(xerr, "protocol", name, "Unrecognized hello element") < 0)
goto done;
clicon_err(OE_XML, EFAULT, "Unrecognized hello element: %s", name);
goto fail;
}
}
goto ok;
}
else if ((strcmp(opname, "notification")) == 0)
goto ok;
else if ((strcmp(opname, "rpc")) == 0)

View file

@ -690,7 +690,7 @@ clixon_xml_parse_file(FILE *fp,
* @param[in,out] xt Pointer to XML parse tree. If empty will be created.
* @param[out] xerr Reason for failure (yang assignment not made) if retval = 0
* @retval 1 Parse OK and all yang assignment made
* @retval 0 Parse OK but yang assigment not made (or only partial)
* @retval 0 Parse OK but yang assigment not made (or only partial), xerr is set
* @retval -1 Error with clicon_err called. Includes parse error
*
* @code

View file

@ -68,6 +68,7 @@
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_yang_module.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_nsctx.h"
/* Undefine if you want to ensure strict namespace assignment on all netconf

View file

@ -170,7 +170,7 @@ xml_parse_version(clixon_xml_yacc *xy,
char *ver)
{
if(strcmp(ver, "1.0")){
clicon_err(OE_XML, XMLPARSE_ERRNO, "Wrong XML version %s expected 1.0", ver);
clicon_err(OE_XML, XMLPARSE_ERRNO, "Unsupported XML version: %s expected 1.0", ver);
free(ver);
return -1;
}
@ -179,6 +179,31 @@ xml_parse_version(clixon_xml_yacc *xy,
return 0;
}
/*! Parse XML encoding
* From under Encoding Declaration:
* In an encoding declaration, the values UTF-8, UTF-16, ISO-10646-UCS-2, and ISO-10646-UCS-4
* SHOULD be used for the various encodings and transformations of Unicode / ISO/IEC 10646, the
* values ISO-8859-1, ISO-8859-2, ... ISO-8859- n (where n is the part number) SHOULD be used for
* the parts of ISO 8859, and the values ISO-2022-JP, Shift_JIS, and EUC-JP " SHOULD be used for
* the various encoded forms of JIS X-0208-1997.
* [UTF-8 is default]
* Note that since ASCII is a subset of UTF-8, ordinary ASCII entities do not strictly need an
* encoding declaration.
*
* Clixon supports only UTF-8 (or no declaration)
*/
static int
xml_parse_encoding(clixon_xml_yacc *xy,
char *enc)
{
if(strcmp(enc, "UTF-8")){
clicon_err(OE_XML, XMLPARSE_ERRNO, "Unsupported XML encoding: %s expected UTF-8", enc);
free(enc);
return -1;
}
return 0;
}
/*! Parse Qualified name -> (Un)PrefixedName
*
* This is where all (parsed) xml elements are created
@ -358,6 +383,7 @@ misc : comment { _PARSE_DEBUG("misc->comment"); }
| WHITESPACE { _PARSE_DEBUG("misc->white space"); }
;
/* [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'*/
xmldcl : BXMLDCL verinfo encodingdecl sddecl EQMARK
{ _PARSE_DEBUG("xmldcl->verinfo encodingdecl? sddecl?"); }
;
@ -370,8 +396,12 @@ verinfo : VER '=' '\"' STRING '\"'
_PARSE_DEBUG("verinfo->version='STRING'");}
;
encodingdecl : ENC '=' '\"' STRING '\"' {if ($4)free($4);}
| ENC '=' '\'' STRING '\'' {if ($4)free($4);}
encodingdecl : ENC '=' '\"' STRING '\"'
{ if (xml_parse_encoding(_XY, $4) <0) YYABORT; if ($4)free($4);
_PARSE_DEBUG("encodingdecl-> encoding = \" STRING \"");}
| ENC '=' '\'' STRING '\''
{ if (xml_parse_encoding(_XY, $4) <0) YYABORT; if ($4)free($4);
_PARSE_DEBUG("encodingdecl-> encoding = ' STRING '");}
|
;

View file

@ -182,19 +182,22 @@ nodetest_eval_node_localonly(cxobj *x,
xpath_tree *xs,
cvec *nsc)
{
int retval = -1;
int retval = -1;
char *name1 = xml_name(x);
char *name2 = NULL;
/* Namespaces is s0, name is s1 */
if (strcmp(xs->xs_s1, "*")==0)
return 1;
name2 = xs->xs_s1;
/* Before going into namespaces, check name equality and filter out noteq */
if (strcmp(name1, name2) != 0){
retval = 0; /* no match */
if (strcmp(xs->xs_s1, "*")==0){
retval = 1;
goto done;
}
name2 = xs->xs_s1;
/* Before going into namespaces, check name equality and filter out noteq */
if (strcmp(name1, name2) == 0){
retval = 1;
goto done;
}
retval = 0; /* no match */
done: /* retval set in preceding statement */
return retval;
}