Unified netconf input function
First for external use, later internal
This commit is contained in:
parent
04d5f52d90
commit
e7c9f3d075
12 changed files with 534 additions and 32 deletions
|
|
@ -73,6 +73,10 @@ Developers may need to change their code
|
||||||
|
|
||||||
### Minor features
|
### Minor features
|
||||||
|
|
||||||
|
* Unified netconf input function
|
||||||
|
* Three different implementations were used in external, internal and controller code
|
||||||
|
* The new clixon_netconf_input API unifies all three uses
|
||||||
|
* Code still experimental controlled by `NEW_NETCONF_INPUT`
|
||||||
* RFC 8528 YANG schema mount
|
* RFC 8528 YANG schema mount
|
||||||
* Made cli/autocli mount-point-aware
|
* Made cli/autocli mount-point-aware
|
||||||
* Internal NETCONF (client <-> backend)
|
* Internal NETCONF (client <-> backend)
|
||||||
|
|
|
||||||
|
|
@ -77,10 +77,13 @@
|
||||||
/* clixon-data value to save buffer between invocations.
|
/* clixon-data value to save buffer between invocations.
|
||||||
* Saving data may be necessary if socket buffer contains partial netconf messages, such as:
|
* Saving data may be necessary if socket buffer contains partial netconf messages, such as:
|
||||||
* <foo/> ..wait 1min ]]>]]>
|
* <foo/> ..wait 1min ]]>]]>
|
||||||
|
* XXX move to data
|
||||||
*/
|
*/
|
||||||
#define NETCONF_HASH_BUF "netconf_input_cbuf"
|
/* Unfinished frame */
|
||||||
#define NETCONF_FRAME_STATE "netconf_input_frame_state"
|
#define NETCONF_FRAME_MSG "netconf-frame-msg"
|
||||||
#define NETCONF_FRAME_SIZE "netconf_input_frame_size"
|
|
||||||
|
#define NETCONF_FRAME_STATE "netconf-input-frame-state"
|
||||||
|
#define NETCONF_FRAME_SIZE "netconf-input-frame-size"
|
||||||
|
|
||||||
/*! Ignore errors on packet errors: continue */
|
/*! Ignore errors on packet errors: continue */
|
||||||
static int ignore_packet_errors = 1;
|
static int ignore_packet_errors = 1;
|
||||||
|
|
@ -149,6 +152,7 @@ netconf_hello_msg(clicon_handle h,
|
||||||
int foundbase_11 = 0;
|
int foundbase_11 = 0;
|
||||||
char *body;
|
char *body;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
_netconf_hello_nr++;
|
_netconf_hello_nr++;
|
||||||
if (xml_find_type(xn, NULL, "session-id", CX_ELMNT) != NULL) {
|
if (xml_find_type(xn, NULL, "session-id", CX_ELMNT) != NULL) {
|
||||||
clicon_err(OE_XML, errno, "Server received hello with session-id from client, terminating (see RFC 6241 Sec 8.1");
|
clicon_err(OE_XML, errno, "Server received hello with session-id from client, terminating (see RFC 6241 Sec 8.1");
|
||||||
|
|
@ -166,12 +170,15 @@ netconf_hello_msg(clicon_handle h,
|
||||||
continue;
|
continue;
|
||||||
/* When comparing protocol version capability URIs, only the base part is used, in the
|
/* When comparing protocol version capability URIs, only the base part is used, in the
|
||||||
* event any parameters are encoded at the end of the URI string. */
|
* event any parameters are encoded at the end of the URI string. */
|
||||||
if (strncmp(body, NETCONF_BASE_CAPABILITY_1_0, strlen(NETCONF_BASE_CAPABILITY_1_0)) == 0) /* RFC 4741 */
|
if (strncmp(body, NETCONF_BASE_CAPABILITY_1_0, strlen(NETCONF_BASE_CAPABILITY_1_0)) == 0){ /* RFC 4741 */
|
||||||
foundbase_10++;
|
foundbase_10++;
|
||||||
|
clicon_debug(1, "%s foundbase10", __FUNCTION__);
|
||||||
|
}
|
||||||
else if (strncmp(body, NETCONF_BASE_CAPABILITY_1_1, strlen(NETCONF_BASE_CAPABILITY_1_1)) == 0 &&
|
else if (strncmp(body, NETCONF_BASE_CAPABILITY_1_1, strlen(NETCONF_BASE_CAPABILITY_1_1)) == 0 &&
|
||||||
clicon_option_int(h, "CLICON_NETCONF_BASE_CAPABILITY") > 0){ /* RFC 6241 */
|
clicon_option_int(h, "CLICON_NETCONF_BASE_CAPABILITY") > 0){ /* RFC 6241 */
|
||||||
foundbase_11++;
|
foundbase_11++;
|
||||||
clicon_data_int_set(h, "netconf-framing", NETCONF_SSH_CHUNKED); /* enable chunked enc */
|
clicon_debug(1, "%s foundbase11", __FUNCTION__);
|
||||||
|
clicon_data_int_set(h, NETCONF_FRAMING_TYPE, NETCONF_SSH_CHUNKED); /* enable chunked enc */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +215,7 @@ netconf_rpc_message(clicon_handle h,
|
||||||
cxobj *xc;
|
cxobj *xc;
|
||||||
netconf_framing_type framing;
|
netconf_framing_type framing;
|
||||||
|
|
||||||
framing = clicon_data_int_get(h, "netconf-framing");
|
framing = clicon_data_int_get(h, NETCONF_FRAMING_TYPE);
|
||||||
if (_netconf_hello_nr == 0 &&
|
if (_netconf_hello_nr == 0 &&
|
||||||
clicon_option_bool(h, "CLICON_NETCONF_HELLO_OPTIONAL") == 0){
|
clicon_option_bool(h, "CLICON_NETCONF_HELLO_OPTIONAL") == 0){
|
||||||
if (netconf_operation_failed_xml(&xret, "rpc", "Client must send an hello element before any RPC")< 0)
|
if (netconf_operation_failed_xml(&xret, "rpc", "Client must send an hello element before any RPC")< 0)
|
||||||
|
|
@ -319,9 +326,10 @@ netconf_input_packet(clicon_handle h,
|
||||||
netconf_framing_type framing;
|
netconf_framing_type framing;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
clicon_debug_xml(1, xreq, "%s", __FUNCTION__);
|
||||||
rpcname = xml_name(xreq);
|
rpcname = xml_name(xreq);
|
||||||
rpcprefix = xml_prefix(xreq);
|
rpcprefix = xml_prefix(xreq);
|
||||||
framing = clicon_data_int_get(h, "netconf-framing");
|
framing = clicon_data_int_get(h, NETCONF_FRAMING_TYPE);
|
||||||
if (xml2ns(xreq, rpcprefix, &namespace) < 0)
|
if (xml2ns(xreq, rpcprefix, &namespace) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (strcmp(rpcname, "rpc") == 0){
|
if (strcmp(rpcname, "rpc") == 0){
|
||||||
|
|
@ -365,11 +373,14 @@ netconf_input_packet(clicon_handle h,
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
if (cbret)
|
if (cbret)
|
||||||
cbuf_free(cbret);
|
cbuf_free(cbret);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NETCONF_INPUT_UNIFIED_EXTERN
|
||||||
/*! Process incoming frame, ie a char message framed by ]]>]]>
|
/*! Process incoming frame, ie a char message framed by ]]>]]>
|
||||||
* Parse string to xml, check only one netconf message within a frame
|
* Parse string to xml, check only one netconf message within a frame
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
|
|
@ -390,7 +401,7 @@ netconf_input_packet(clicon_handle h,
|
||||||
* - RPC messages: send rpc-error
|
* - RPC messages: send rpc-error
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
netconf_input_frame(clicon_handle h,
|
netconf_input_frame1(clicon_handle h,
|
||||||
cbuf *cb,
|
cbuf *cb,
|
||||||
int *eof)
|
int *eof)
|
||||||
{
|
{
|
||||||
|
|
@ -406,7 +417,7 @@ netconf_input_frame(clicon_handle h,
|
||||||
|
|
||||||
clicon_debug(CLIXON_DBG_DETAIL, "%s", __FUNCTION__);
|
clicon_debug(CLIXON_DBG_DETAIL, "%s", __FUNCTION__);
|
||||||
clicon_debug(CLIXON_DBG_MSG, "Recv ext: %s", cbuf_get(cb));
|
clicon_debug(CLIXON_DBG_MSG, "Recv ext: %s", cbuf_get(cb));
|
||||||
framing = clicon_data_int_get(h, "netconf-framing");
|
framing = clicon_data_int_get(h, NETCONF_FRAMING_TYPE);
|
||||||
yspec = clicon_dbspec_yang(h);
|
yspec = clicon_dbspec_yang(h);
|
||||||
if ((str = strdup(cbuf_get(cb))) == NULL){
|
if ((str = strdup(cbuf_get(cb))) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "strdup");
|
clicon_err(OE_UNIX, errno, "strdup");
|
||||||
|
|
@ -504,6 +515,7 @@ netconf_input_frame(clicon_handle h,
|
||||||
cbuf_free(cbret);
|
cbuf_free(cbret);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
#endif /* NETCONF_INPUT_UNIFIED_EXTERN */
|
||||||
|
|
||||||
/*! Get netconf message: detect end-of-msg
|
/*! Get netconf message: detect end-of-msg
|
||||||
* @param[in] s Socket where input arrived. read from this.
|
* @param[in] s Socket where input arrived. read from this.
|
||||||
|
|
@ -511,11 +523,144 @@ netconf_input_frame(clicon_handle h,
|
||||||
* This routine continuously reads until no more data on s. There could
|
* This routine continuously reads until no more data on s. There could
|
||||||
* be risk of starvation, but the netconf client does little else than
|
* be risk of starvation, but the netconf client does little else than
|
||||||
* read data so I do not see a danger of true starvation here.
|
* read data so I do not see a danger of true starvation here.
|
||||||
* @note data is saved in clicon-handle at NETCONF_HASH_BUF since there is a potential issue if data
|
* @note data is saved in clicon-handle at NETCONF_FRAME_MSG since there is a potential issue if data
|
||||||
* is not completely present on the s, ie if eg:
|
* is not completely present on the s, ie if eg:
|
||||||
* <a>foo ..pause.. </a>]]>]]>
|
* <a>foo ..pause.. </a>]]>]]>
|
||||||
* then only "</a>" would be delivered to netconf_input_frame().
|
* then only "</a>" would be delivered to netconf_input_frame().
|
||||||
*/
|
*/
|
||||||
|
#ifdef NETCONF_INPUT_UNIFIED_EXTERN
|
||||||
|
static int
|
||||||
|
netconf_input_cb(int s,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
clicon_handle h = arg;
|
||||||
|
cbuf *cbmsg=NULL;
|
||||||
|
cbuf *cberr = NULL;
|
||||||
|
void *ptr;
|
||||||
|
yang_stmt *yspec;
|
||||||
|
clicon_hash_t *cdat = clicon_data(h); /* Save cbuf between calls if not done */
|
||||||
|
size_t cdatlen = 0;
|
||||||
|
int frame_state;
|
||||||
|
size_t frame_size;
|
||||||
|
int i32;
|
||||||
|
int eom = 0;
|
||||||
|
int eof = 0;
|
||||||
|
int framing_type;
|
||||||
|
cxobj *xtop = NULL;
|
||||||
|
cxobj *xreq;
|
||||||
|
cxobj *xerr = NULL;
|
||||||
|
int ret;
|
||||||
|
unsigned char buf[BUFSIZ]; /* from stdio.h, typically 8K */
|
||||||
|
ssize_t buflen = sizeof(buf);
|
||||||
|
unsigned char *p = buf;
|
||||||
|
ssize_t len;
|
||||||
|
size_t plen;
|
||||||
|
|
||||||
|
yspec = clicon_dbspec_yang(h);
|
||||||
|
/* Get unfinished frame */
|
||||||
|
if ((ptr = clicon_hash_value(cdat, NETCONF_FRAME_MSG, &cdatlen)) != NULL){
|
||||||
|
if (cdatlen != sizeof(cbmsg)){
|
||||||
|
clicon_err(OE_XML, errno, "size mismatch %lu %lu",
|
||||||
|
(unsigned long)cdatlen, (unsigned long)sizeof(cbmsg));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
cbmsg = *(cbuf**)ptr;
|
||||||
|
clicon_hash_del(cdat, NETCONF_FRAME_MSG);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if ((cbmsg = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((frame_state = clicon_data_int_get(h, NETCONF_FRAME_STATE)) < 0)
|
||||||
|
frame_state = 0;
|
||||||
|
if ((i32 = clicon_data_int_get(h, NETCONF_FRAME_SIZE)) < 0)
|
||||||
|
frame_size = 0;
|
||||||
|
else
|
||||||
|
frame_size = i32;
|
||||||
|
/* Read input data from socket and append to cbbuf */
|
||||||
|
if ((len = netconf_input_read2(s, buf, buflen, &eof)) < 0)
|
||||||
|
goto done;
|
||||||
|
p = buf;
|
||||||
|
plen = len;
|
||||||
|
while (!eof && plen > 0){
|
||||||
|
framing_type = clicon_data_int_get(h, NETCONF_FRAMING_TYPE); /* Can be set in frame handler */
|
||||||
|
if (netconf_input_msg2(&p, &plen,
|
||||||
|
cbmsg,
|
||||||
|
framing_type,
|
||||||
|
&frame_state,
|
||||||
|
&frame_size,
|
||||||
|
&eom) < 0)
|
||||||
|
goto done;
|
||||||
|
if (eom == 0){ /* frame not complete */
|
||||||
|
clicon_debug(CLIXON_DBG_DETAIL, "%s: frame: %lu", __FUNCTION__, cbuf_len(cbmsg));
|
||||||
|
/* Extra data to read, save data and continue on next round */
|
||||||
|
if (clicon_hash_add(cdat, NETCONF_FRAME_MSG, &cbmsg, sizeof(cbmsg)) == NULL)
|
||||||
|
goto done;
|
||||||
|
cbmsg = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
clicon_debug(CLIXON_DBG_MSG, "Recv ext: %s", cbuf_get(cbmsg));
|
||||||
|
if ((ret = netconf_input_frame2(cbmsg, Y_RPC, yspec, &xtop, &xerr)) < 0)
|
||||||
|
goto done;
|
||||||
|
cbuf_reset(cbmsg);
|
||||||
|
if (ret == 0){ /* Invalid frame, parse error, etc */
|
||||||
|
if ((cberr = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (clixon_xml2cbuf(cberr, xerr, 0, 0, NULL, -1, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xerr){
|
||||||
|
xml_free(xerr);
|
||||||
|
xerr = NULL;
|
||||||
|
}
|
||||||
|
if (netconf_output_encap(framing_type, cberr) < 0)
|
||||||
|
goto done;
|
||||||
|
if (netconf_output(1, cberr, "rpc-error") < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((xreq = xml_child_i_type(xtop, 0, CX_ELMNT)) == NULL){
|
||||||
|
clicon_err(OE_XML, EFAULT, "No xml req (shouldnt happen)");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (netconf_input_packet(h, xreq, yspec, &eof) < 0){
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (xtop){
|
||||||
|
xml_free(xtop);
|
||||||
|
xtop = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (eof){ /* socket closed / read returns 0 */
|
||||||
|
clicon_debug(1, "%s len==0, closing", __FUNCTION__);
|
||||||
|
clixon_event_unreg_fd(s, netconf_input_cb);
|
||||||
|
close(s);
|
||||||
|
clixon_exit_set(1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
clicon_data_int_set(h, NETCONF_FRAME_STATE, frame_state);
|
||||||
|
clicon_data_int_set(h, NETCONF_FRAME_SIZE, frame_size);
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (cbmsg)
|
||||||
|
cbuf_free(cbmsg);
|
||||||
|
if (cberr)
|
||||||
|
cbuf_free(cberr);
|
||||||
|
if (xtop)
|
||||||
|
xml_free(xtop);
|
||||||
|
if (xerr)
|
||||||
|
xml_free(xerr);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* NETCONF_INPUT_UNIFIED_EXTERN */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
netconf_input_cb(int s,
|
netconf_input_cb(int s,
|
||||||
void *arg)
|
void *arg)
|
||||||
|
|
@ -547,14 +692,14 @@ netconf_input_cb(int s,
|
||||||
goto done;
|
goto done;
|
||||||
frame_size = (size_t)ret;
|
frame_size = (size_t)ret;
|
||||||
}
|
}
|
||||||
if ((ptr = clicon_hash_value(cdat, NETCONF_HASH_BUF, &cdatlen)) != NULL){
|
if ((ptr = clicon_hash_value(cdat, NETCONF_FRAME_MSG, &cdatlen)) != NULL){
|
||||||
if (cdatlen != sizeof(cb)){
|
if (cdatlen != sizeof(cb)){
|
||||||
clicon_err(OE_XML, errno, "size mismatch %lu %lu",
|
clicon_err(OE_XML, errno, "size mismatch %lu %lu",
|
||||||
(unsigned long)cdatlen, (unsigned long)sizeof(cb));
|
(unsigned long)cdatlen, (unsigned long)sizeof(cb));
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cb = *(cbuf**)ptr;
|
cb = *(cbuf**)ptr;
|
||||||
clicon_hash_del(cdat, NETCONF_HASH_BUF);
|
clicon_hash_del(cdat, NETCONF_FRAME_MSG);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((cb = cbuf_new()) == NULL){
|
||||||
|
|
@ -582,7 +727,7 @@ netconf_input_cb(int s,
|
||||||
for (i=0; i<len; i++){
|
for (i=0; i<len; i++){
|
||||||
if (buf[i] == 0)
|
if (buf[i] == 0)
|
||||||
continue; /* Skip NULL chars (eg from terminals) */
|
continue; /* Skip NULL chars (eg from terminals) */
|
||||||
if (clicon_data_int_get(h, "netconf-framing") == NETCONF_SSH_CHUNKED){
|
if (clicon_data_int_get(h, NETCONF_FRAMING_TYPE) == NETCONF_SSH_CHUNKED){
|
||||||
/* Track chunked framing defined in RFC6242 */
|
/* Track chunked framing defined in RFC6242 */
|
||||||
if ((ret = netconf_input_chunked_framing(buf[i], &frame_state, &frame_size)) < 0)
|
if ((ret = netconf_input_chunked_framing(buf[i], &frame_state, &frame_size)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -594,7 +739,7 @@ netconf_input_cb(int s,
|
||||||
/* Somewhat complex error-handling:
|
/* Somewhat complex error-handling:
|
||||||
* Ignore packet errors, UNLESS an explicit termination request (eof)
|
* Ignore packet errors, UNLESS an explicit termination request (eof)
|
||||||
*/
|
*/
|
||||||
if (netconf_input_frame(h, cb, &eof) < 0 &&
|
if (netconf_input_frame1(h, cb, &eof) < 0 &&
|
||||||
!ignore_packet_errors)
|
!ignore_packet_errors)
|
||||||
goto done;
|
goto done;
|
||||||
if (eof)
|
if (eof)
|
||||||
|
|
@ -612,7 +757,7 @@ netconf_input_cb(int s,
|
||||||
/* OK, we have an xml string from a client */
|
/* OK, we have an xml string from a client */
|
||||||
/* Remove trailer */
|
/* Remove trailer */
|
||||||
*(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0';
|
*(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0';
|
||||||
if (netconf_input_frame(h, cb, &eof) < 0 &&
|
if (netconf_input_frame1(h, cb, &eof) < 0 &&
|
||||||
!ignore_packet_errors) // default is to ignore errors
|
!ignore_packet_errors) // default is to ignore errors
|
||||||
goto done;
|
goto done;
|
||||||
if (eof)
|
if (eof)
|
||||||
|
|
@ -627,7 +772,7 @@ netconf_input_cb(int s,
|
||||||
if (poll == 0){
|
if (poll == 0){
|
||||||
/* No data to read, save data and continue on next round */
|
/* No data to read, save data and continue on next round */
|
||||||
if (cbuf_len(cb) != 0){
|
if (cbuf_len(cb) != 0){
|
||||||
if (clicon_hash_add(cdat, NETCONF_HASH_BUF, &cb, sizeof(cb)) == NULL)
|
if (clicon_hash_add(cdat, NETCONF_FRAME_MSG, &cb, sizeof(cb)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
cb = NULL;
|
cb = NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -643,6 +788,7 @@ netconf_input_cb(int s,
|
||||||
cbuf_free(cb);
|
cbuf_free(cb);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
#endif /* NETCONF_INPUT_UNIFIED_EXTERN */
|
||||||
|
|
||||||
/*! Send netconf hello message
|
/*! Send netconf hello message
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
|
|
@ -663,7 +809,7 @@ send_hello(clicon_handle h,
|
||||||
}
|
}
|
||||||
if (netconf_hello_server(h, cb, id) < 0)
|
if (netconf_hello_server(h, cb, id) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
framing = clicon_data_int_get(h, "netconf-framing");
|
framing = clicon_data_int_get(h, NETCONF_FRAMING_TYPE);
|
||||||
if (netconf_output_encap(framing, cb) < 0)
|
if (netconf_output_encap(framing, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (netconf_output(s, cb, "hello") < 0)
|
if (netconf_output(s, cb, "hello") < 0)
|
||||||
|
|
@ -1021,7 +1167,6 @@ main(int argc,
|
||||||
if (clixon_event_reg_timeout(t, timeout_fn, NULL, "timeout") < 0)
|
if (clixon_event_reg_timeout(t, timeout_fn, NULL, "timeout") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clixon_event_loop(h) < 0)
|
if (clixon_event_loop(h) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
|
||||||
|
|
@ -482,7 +482,7 @@ netconf_notification_cb(int s,
|
||||||
if (clixon_xml2cbuf(cb, xn, 0, 0, NULL, -1, 0) < 0)
|
if (clixon_xml2cbuf(cb, xn, 0, 0, NULL, -1, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Send it to listening client on stdout */
|
/* Send it to listening client on stdout */
|
||||||
if (netconf_output_encap(clicon_option_int(h, "netconf-framing"), cb) < 0){
|
if (netconf_output_encap(clicon_data_int_get(h, NETCONF_FRAMING_TYPE), cb) < 0){
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (netconf_output(1, cb, "notification") < 0){
|
if (netconf_output(1, cb, "notification") < 0){
|
||||||
|
|
|
||||||
|
|
@ -201,3 +201,22 @@
|
||||||
* Introduced in 6.1, remove in 6.2
|
* Introduced in 6.1, remove in 6.2
|
||||||
*/
|
*/
|
||||||
#define AUTOCLI_DEPRECATED_HIDE
|
#define AUTOCLI_DEPRECATED_HIDE
|
||||||
|
|
||||||
|
/*! Unified netconf input function
|
||||||
|
* Replace external, internal and controller netconf eventually
|
||||||
|
* New file: clixon_netconf_input.c with functions:
|
||||||
|
* - netconf_input_read2
|
||||||
|
* - netconf_input_msg2
|
||||||
|
* - netconf_input_frame2
|
||||||
|
* The following code should use this:
|
||||||
|
* X - netconf_main.c external netconf
|
||||||
|
* - netconf_proto.c internal netconf
|
||||||
|
* - controller device control
|
||||||
|
*/
|
||||||
|
/*! Use unified netconf input function for external use
|
||||||
|
*/
|
||||||
|
#define NETCONF_INPUT_UNIFIED_EXTERN
|
||||||
|
|
||||||
|
/*! Use unified netconf input function for internal use
|
||||||
|
*/
|
||||||
|
#undef NETCONF_INPUT_UNIFIED_INTERNAL
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ extern "C" {
|
||||||
#include <clixon/clixon_stream.h>
|
#include <clixon/clixon_stream.h>
|
||||||
#include <clixon/clixon_proto.h>
|
#include <clixon/clixon_proto.h>
|
||||||
#include <clixon/clixon_netconf_lib.h>
|
#include <clixon/clixon_netconf_lib.h>
|
||||||
|
#include <clixon/clixon_netconf_input.h>
|
||||||
#include <clixon/clixon_proto_client.h>
|
#include <clixon/clixon_proto_client.h>
|
||||||
#include <clixon/clixon_plugin.h>
|
#include <clixon/clixon_plugin.h>
|
||||||
#include <clixon/clixon_options.h>
|
#include <clixon/clixon_options.h>
|
||||||
|
|
|
||||||
61
lib/clixon/clixon_netconf_input.h
Normal file
61
lib/clixon/clixon_netconf_input.h
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
||||||
|
|
||||||
|
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 *****
|
||||||
|
* Netconf input routines
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CLIXON_NETCONF_INPUT_H
|
||||||
|
#define _CLIXON_NETCONF_INPUT_H
|
||||||
|
|
||||||
|
/* What kind of framing? NETCONF_SSH_EOM or NETCONF_SSH_CHUNKED
|
||||||
|
* Global setting used with clicon_data_int_get()
|
||||||
|
*/
|
||||||
|
#define NETCONF_FRAMING_TYPE "netconf-framing-type"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prototypes
|
||||||
|
*/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ssize_t netconf_input_read2(int s, unsigned char *buf, ssize_t buflen, int *eof);
|
||||||
|
int netconf_input_msg2(unsigned char **bufp, size_t *lenp, cbuf *cbmsg,
|
||||||
|
netconf_framing_type framing, int *frame_state, size_t *frame_size,
|
||||||
|
int *eom);
|
||||||
|
int netconf_input_frame2(cbuf *cb, yang_bind yb, yang_stmt *yspec, cxobj **xrecv, cxobj **xerr);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _CLIXON_NETCONF_INPUT_H */
|
||||||
|
|
@ -92,7 +92,8 @@ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
|
||||||
clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_xpath_function.c \
|
clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_xpath_function.c \
|
||||||
clixon_xpath_optimize.c clixon_xpath_yang.c \
|
clixon_xpath_optimize.c clixon_xpath_yang.c \
|
||||||
clixon_datastore.c clixon_datastore_write.c clixon_datastore_read.c \
|
clixon_datastore.c clixon_datastore_write.c clixon_datastore_read.c \
|
||||||
clixon_netconf_lib.c clixon_stream.c clixon_nacm.c clixon_client.c clixon_netns.c \
|
clixon_netconf_lib.c clixon_netconf_input.c clixon_stream.c \
|
||||||
|
clixon_nacm.c clixon_client.c clixon_netns.c \
|
||||||
clixon_dispatcher.c clixon_text_syntax.c
|
clixon_dispatcher.c clixon_text_syntax.c
|
||||||
|
|
||||||
YACCOBJS = lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
|
YACCOBJS = lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
|
||||||
|
|
|
||||||
270
lib/src/clixon_netconf_input.c
Normal file
270
lib/src/clixon_netconf_input.c
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright (C) 2023 Olof Hagsand
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
* Unified netconf input routines
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
|
||||||
|
if ((len = netconf_input_read2(s, buf, buflen, &eof)) < 0)
|
||||||
|
goto done;
|
||||||
|
p = buf; plen = len;
|
||||||
|
while (!eof){
|
||||||
|
if (netconf_input_msg2(&p, &plen, cbmsg, framing_type, &frame_state, &frame_size, &eom) < 0)
|
||||||
|
goto done;
|
||||||
|
if (!eom)
|
||||||
|
break;
|
||||||
|
if ((ret = netconf_input_frame2(cbmsg, Y_RPC, yspec, &cbret, &xtop)) < 0)
|
||||||
|
goto done;
|
||||||
|
// process incoming packet xtop
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eom == 0)
|
||||||
|
// frame not complete
|
||||||
|
if ((ret = netconf_input_frame(cb, yspec, &xtop)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0)
|
||||||
|
// invalid
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <fnmatch.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
/* clicon */
|
||||||
|
#include <cligen/cligen.h>
|
||||||
|
|
||||||
|
/* Local includes */
|
||||||
|
#include "clixon_queue.h"
|
||||||
|
#include "clixon_hash.h"
|
||||||
|
#include "clixon_string.h"
|
||||||
|
#include "clixon_err.h"
|
||||||
|
#include "clixon_handle.h"
|
||||||
|
#include "clixon_yang.h"
|
||||||
|
#include "clixon_log.h"
|
||||||
|
#include "clixon_xml.h"
|
||||||
|
#include "clixon_xml_io.h"
|
||||||
|
#include "clixon_proto.h"
|
||||||
|
#include "clixon_netconf_lib.h"
|
||||||
|
#include "clixon_netconf_input.h"
|
||||||
|
|
||||||
|
/*! Read from socket and append to cbuf
|
||||||
|
*
|
||||||
|
* @param[in] s Socket where input arrives. Read from this.
|
||||||
|
* @param[in] buf Packet buffer
|
||||||
|
* @param[in] buflen Length of packet buffer
|
||||||
|
* @param[out] eof Socket closed / eof?
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
netconf_input_read2(int s,
|
||||||
|
unsigned char *buf,
|
||||||
|
ssize_t buflen,
|
||||||
|
int *eof)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
memset(buf, 0, buflen);
|
||||||
|
if ((len = read(s, buf, buflen)) < 0){
|
||||||
|
if (errno == ECONNRESET)
|
||||||
|
len = 0; /* emulate EOF */
|
||||||
|
else{
|
||||||
|
clicon_log(LOG_ERR, "%s: read: %s", __FUNCTION__, strerror(errno));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
} /* read */
|
||||||
|
clicon_debug(CLIXON_DBG_DETAIL, "%s len:%ld", __FUNCTION__, len);
|
||||||
|
if (len == 0){ /* EOF */
|
||||||
|
clicon_debug(CLIXON_DBG_DETAIL, "%s len==0, closing", __FUNCTION__);
|
||||||
|
*eof = 1;
|
||||||
|
}
|
||||||
|
retval = len;
|
||||||
|
done:
|
||||||
|
clicon_debug(CLIXON_DBG_DETAIL, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Get netconf message using NETCONF framing
|
||||||
|
*
|
||||||
|
* @param[in,out] bufp Input data, incremented as read
|
||||||
|
* @param[in,out] lenp Data len, decremented as read
|
||||||
|
* @param[in,out] cbmsg Completed frame (if eom), may contain data on entry
|
||||||
|
* @param[in] framing_type EOM or chunked framing
|
||||||
|
* @param[in,out] frame_state Framing state depending on type
|
||||||
|
* @param[in,out] frame_size Chunked framing size parameter
|
||||||
|
* @param[out] eom If frame found in cb?
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* The routine should be called continuously with more data from input socket in buf
|
||||||
|
* State of previous reads is saved in:
|
||||||
|
* - bufp/lenp
|
||||||
|
* - cbmsg
|
||||||
|
* - frame_state/frame_size
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
netconf_input_msg2(unsigned char **bufp,
|
||||||
|
size_t *lenp,
|
||||||
|
cbuf *cbmsg,
|
||||||
|
netconf_framing_type framing_type,
|
||||||
|
int *frame_state,
|
||||||
|
size_t *frame_size,
|
||||||
|
int *eom)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
int i;
|
||||||
|
int ret;
|
||||||
|
int found = 0;
|
||||||
|
size_t len;
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
clicon_debug(CLIXON_DBG_DETAIL, "%s", __FUNCTION__);
|
||||||
|
len = *lenp;
|
||||||
|
for (i=0; i<len; i++){
|
||||||
|
if ((ch = (*bufp)[i]) == 0)
|
||||||
|
continue; /* Skip NULL chars (eg from terminals) */
|
||||||
|
if (framing_type == NETCONF_SSH_CHUNKED){
|
||||||
|
/* Track chunked framing defined in RFC6242 */
|
||||||
|
if ((ret = netconf_input_chunked_framing(ch, frame_state, frame_size)) < 0)
|
||||||
|
goto done;
|
||||||
|
switch (ret){
|
||||||
|
case 1: /* chunk-data */
|
||||||
|
cprintf(cbmsg, "%c", ch);
|
||||||
|
break;
|
||||||
|
case 2: /* end-of-data */
|
||||||
|
/* Somewhat complex error-handling:
|
||||||
|
* Ignore packet errors, UNLESS an explicit termination request (eof)
|
||||||
|
*/
|
||||||
|
found++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
cprintf(cbmsg, "%c", ch);
|
||||||
|
if (detect_endtag("]]>]]>", ch, frame_state)){
|
||||||
|
*frame_state = 0;
|
||||||
|
/* OK, we have an xml string from a client */
|
||||||
|
/* Remove trailer */
|
||||||
|
*(((char*)cbuf_get(cbmsg)) + cbuf_len(cbmsg) - strlen("]]>]]>")) = '\0';
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found){
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} /* for */
|
||||||
|
*bufp += i;
|
||||||
|
*lenp -= i;
|
||||||
|
*eom = found;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
clicon_debug(CLIXON_DBG_DETAIL, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Process incoming frame, ie a char message framed by ]]>]]>
|
||||||
|
*
|
||||||
|
* Parse string to xml, check only one netconf message within a frame
|
||||||
|
* @param[in] cb Packet buffer
|
||||||
|
* @param[in] yb Yang binding: Y_RPC for server-side, Y_NONE for client-side (for now)
|
||||||
|
* @param[in] yspec Yang spec
|
||||||
|
* @param[out] xrecv XML packet
|
||||||
|
* @param[out] xerr XML error, (if ret = 0)
|
||||||
|
* @retval 1 OK
|
||||||
|
* @retval 0 Invalid, parse error, etc, xerr points to netconf error message
|
||||||
|
* @retval -1 Fatal error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
netconf_input_frame2(cbuf *cb,
|
||||||
|
yang_bind yb,
|
||||||
|
yang_stmt *yspec,
|
||||||
|
cxobj **xrecv,
|
||||||
|
cxobj **xerr)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *str = NULL;
|
||||||
|
cxobj *xtop = NULL; /* Request (in) */
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
clicon_debug(CLIXON_DBG_DETAIL, "%s", __FUNCTION__);
|
||||||
|
if (xrecv == NULL){
|
||||||
|
clicon_err(OE_PLUGIN, EINVAL, "xrecv is NULL");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
str = cbuf_get(cb);
|
||||||
|
/* Special case: empty XML */
|
||||||
|
if (strlen(str) == 0){
|
||||||
|
if (netconf_operation_failed_xml(xerr, "rpc", "Empty XML")< 0)
|
||||||
|
goto done;
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
/* Fix to distinguish RPC and REPLIES */
|
||||||
|
if ((ret = clixon_xml_parse_string(str, yb, yspec, &xtop, xerr)) < 0){
|
||||||
|
/* XXX possibly should quit on -1? */
|
||||||
|
if (netconf_operation_failed_xml(xerr, "rpc", clicon_err_reason)< 0)
|
||||||
|
goto done;
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
if (ret == 0){
|
||||||
|
/* Note: xtop can be "hello" in which case one (maybe) should drop the session and log
|
||||||
|
* However, its not until netconf_input_packet that rpc vs hello vs other identification is
|
||||||
|
* actually made.
|
||||||
|
* Actually, there are no error replies to hello messages according to any RFC, so
|
||||||
|
* rpc error reply here is non-standard, but may be useful.
|
||||||
|
*/
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
/* Check for empty frame (no messages), return empty message, not clear from RFC what to do */
|
||||||
|
if (xml_child_nr_type(xtop, CX_ELMNT) == 0){
|
||||||
|
if (netconf_operation_failed_xml(xerr, "rpc", "Truncated XML")< 0)
|
||||||
|
goto done;
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
if (xml_child_nr_type(xtop, CX_ELMNT) != 1){
|
||||||
|
if (netconf_malformed_message_xml(xerr, "More than one message in netconf rpc frame")< 0)
|
||||||
|
goto done;
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
*xrecv = xtop;
|
||||||
|
xtop = NULL;
|
||||||
|
retval = 1;
|
||||||
|
done:
|
||||||
|
if (xtop)
|
||||||
|
xml_free(xtop);
|
||||||
|
return retval;
|
||||||
|
failed:
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -74,8 +74,8 @@
|
||||||
#include "clixon_yang_module.h"
|
#include "clixon_yang_module.h"
|
||||||
#include "clixon_yang_parse_lib.h"
|
#include "clixon_yang_parse_lib.h"
|
||||||
#include "clixon_plugin.h"
|
#include "clixon_plugin.h"
|
||||||
|
|
||||||
#include "clixon_netconf_lib.h"
|
#include "clixon_netconf_lib.h"
|
||||||
|
#include "clixon_netconf_input.h"
|
||||||
|
|
||||||
/* Mapping between RFC6243 withdefaults strings <--> ints
|
/* Mapping between RFC6243 withdefaults strings <--> ints
|
||||||
*/
|
*/
|
||||||
|
|
@ -465,6 +465,7 @@ netconf_unknown_attribute(cbuf *cb,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Common Netconf element XML tree according to RFC 6241 App A
|
/*! Common Netconf element XML tree according to RFC 6241 App A
|
||||||
|
*
|
||||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||||
* @param[in] type Error type: "application" or "protocol"
|
* @param[in] type Error type: "application" or "protocol"
|
||||||
* @param[in] tag Error tag
|
* @param[in] tag Error tag
|
||||||
|
|
@ -495,7 +496,6 @@ netconf_common_xml(cxobj **xret,
|
||||||
}
|
}
|
||||||
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>%s</error-type>"
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>%s</error-type>"
|
||||||
|
|
@ -1631,10 +1631,10 @@ netconf_module_load(clicon_handle h)
|
||||||
* But start with default: RFC 4741 EOM ]]>]]>
|
* But start with default: RFC 4741 EOM ]]>]]>
|
||||||
* For now this only applies to external protocol
|
* For now this only applies to external protocol
|
||||||
*/
|
*/
|
||||||
clicon_data_int_set(h, "netconf-framing", NETCONF_SSH_EOM);
|
clicon_data_int_set(h, NETCONF_FRAMING_TYPE, NETCONF_SSH_EOM);
|
||||||
if (clicon_option_bool(h, "CLICON_NETCONF_HELLO_OPTIONAL")){
|
if (clicon_option_bool(h, "CLICON_NETCONF_HELLO_OPTIONAL")){
|
||||||
if (clicon_option_int(h, "CLICON_NETCONF_BASE_CAPABILITY") > 0) /* RFC 6241 */
|
if (clicon_option_int(h, "CLICON_NETCONF_BASE_CAPABILITY") > 0) /* RFC 6241 */
|
||||||
clicon_data_int_set(h, "netconf-framing", NETCONF_SSH_CHUNKED);
|
clicon_data_int_set(h, NETCONF_FRAMING_TYPE, NETCONF_SSH_CHUNKED);
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -1870,7 +1870,7 @@ netconf_hello_server(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Generate textual error log from Netconf error message
|
/*! Generate and print textual error log from Netconf error message
|
||||||
*
|
*
|
||||||
* Get a text error message from netconf error message and generate error on the form:
|
* Get a text error message from netconf error message and generate error on the form:
|
||||||
* <msg>: "<arg>": <netconf-error> or <msg>: <netconf-error>
|
* <msg>: "<arg>": <netconf-error> or <msg>: <netconf-error>
|
||||||
|
|
|
||||||
|
|
@ -494,7 +494,7 @@ function chunked_equal()
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Given a string, add RFC6242 chunked franing around it
|
# Given a string, add RFC6242 chunked framing around it
|
||||||
# Args:
|
# Args:
|
||||||
# 0: string
|
# 0: string
|
||||||
function chunked_framing()
|
function chunked_framing()
|
||||||
|
|
@ -818,6 +818,7 @@ function expecteof(){
|
||||||
retval=$2
|
retval=$2
|
||||||
input=$3
|
input=$3
|
||||||
expect=$4
|
expect=$4
|
||||||
|
|
||||||
if [ $# -gt 4 ]; then
|
if [ $# -gt 4 ]; then
|
||||||
errfile=$(mktemp)
|
errfile=$(mktemp)
|
||||||
expecterr=$5
|
expecterr=$5
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "<rpc $DEFAULTONLY><ge
|
||||||
|
|
||||||
#<capability>urn:ietf:params:netconf:base:1.1</capability>
|
#<capability>urn:ietf:params:netconf:base:1.1</capability>
|
||||||
new "netconf rcv hello, disable RFC7895/ietf-yang-library"
|
new "netconf rcv hello, disable RFC7895/ietf-yang-library"
|
||||||
expecteof_netconf "$clixon_netconf -f $cfg -o CLICON_YANG_LIBRARY=0" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data/></rpc-reply>"
|
expecteof_netconf "$clixon_netconf -qD 7 -lf/tmp/netconf0.log -f $cfg -o CLICON_YANG_LIBRARY=0" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data/></rpc-reply>"
|
||||||
|
|
||||||
new "netconf get-config nc prefix"
|
new "netconf get-config nc prefix"
|
||||||
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<nc:rpc xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:message-id=\"42\"><nc:get-config><nc:source><nc:candidate/></nc:source></nc:get-config></nc:rpc>" "" "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:message-id=\"42\"><data/></rpc-reply>"
|
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<nc:rpc xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:message-id=\"42\"><nc:get-config><nc:source><nc:candidate/></nc:source></nc:get-config></nc:rpc>" "" "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:message-id=\"42\"><data/></rpc-reply>"
|
||||||
|
|
|
||||||
|
|
@ -118,14 +118,14 @@ expecteof "$clixon_netconf -f $cfg" 0 "<?xml version=\"1.0\" encoding=\"UTF-8\"?
|
||||||
|
|
||||||
# Actually non-standard to reply on wrong hello with rpc-error, but may be useful
|
# Actually non-standard to reply on wrong hello with rpc-error, but may be useful
|
||||||
new "Netconf snd hello with extra element"
|
new "Netconf snd hello with extra element"
|
||||||
expecteof "$clixon_netconf -qef $cfg" 0 "<hello $DEFAULTONLY><capabilities><extra-element/><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>" '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><rpc-error><error-type>protocol</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>extra-element</bad-element></error-info><error-severity>error</error-severity><error-message>Unrecognized hello/capabilities element</error-message></rpc-error></rpc-reply>]]>]]>$' '^$'
|
expecteof "$clixon_netconf -qf $cfg" 0 "<hello $DEFAULTONLY><capabilities><extra-element/><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>" '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><rpc-error><error-type>protocol</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>extra-element</bad-element></error-info><error-severity>error</error-severity><error-message>Unrecognized hello/capabilities element</error-message></rpc-error></rpc-reply>]]>]]>$' '^$'
|
||||||
|
|
||||||
new "Netconf send rpc without hello error"
|
new "Netconf send rpc without hello error"
|
||||||
expecteof "$clixon_netconf -qef $cfg" 255 "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><rpc-error><error-type>rpc</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Client must send an hello element before any RPC</error-message></rpc-error></rpc-reply>]]>]]>" '^$'
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "<rpc-reply $DEFAULTNS><rpc-error><error-type>rpc</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Client must send an hello element before any RPC</error-message></rpc-error></rpc-reply>]]>]]>" '^$'
|
||||||
|
|
||||||
# same as -H
|
# same as -H
|
||||||
new "Netconf send rpc without hello w CLICON_NETCONF_HELLO_OPTIONAL"
|
new "Netconf send rpc without hello w CLICON_NETCONF_HELLO_OPTIONAL"
|
||||||
expecteof_netconf "$clixon_netconf -qef $cfg -o CLICON_NETCONF_HELLO_OPTIONAL=true" 0 "" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data/></rpc-reply>"
|
expecteof_netconf "$clixon_netconf -qf $cfg -o CLICON_NETCONF_HELLO_OPTIONAL=true" 0 "" "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>" "" "<rpc-reply $DEFAULTNS><data/></rpc-reply>"
|
||||||
|
|
||||||
if [ $BE -ne 0 ]; then
|
if [ $BE -ne 0 ]; then
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue