2339 lines
74 KiB
C
2339 lines
74 KiB
C
/*
|
|
*
|
|
***** BEGIN LICENSE BLOCK *****
|
|
|
|
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
|
Copyright (C) 2017-2019 Olof Hagsand
|
|
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 library functions. See RFC6241
|
|
* Functions to generate a netconf error message come in two forms: xml-tree and
|
|
* cbuf. XML tree is preferred.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "clixon_config.h" /* generated by config & autoconf */
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
#include <syslog.h>
|
|
#include <sys/param.h>
|
|
|
|
/* cligen */
|
|
#include <cligen/cligen.h>
|
|
|
|
/* clixon */
|
|
#include "clixon_queue.h"
|
|
#include "clixon_hash.h"
|
|
#include "clixon_string.h"
|
|
#include "clixon_handle.h"
|
|
#include "clixon_yang.h"
|
|
#include "clixon_xml.h"
|
|
#include "clixon_err.h"
|
|
#include "clixon_log.h"
|
|
#include "clixon_debug.h"
|
|
#include "clixon_options.h"
|
|
#include "clixon_data.h"
|
|
#include "clixon_xml_bind.h"
|
|
#include "clixon_xml_map.h"
|
|
#include "clixon_netconf_lib.h"
|
|
#include "clixon_xml_io.h"
|
|
#include "clixon_xpath_ctx.h"
|
|
#include "clixon_xpath.h"
|
|
#include "clixon_yang_module.h"
|
|
#include "clixon_yang_parse_lib.h"
|
|
#include "clixon_plugin.h"
|
|
#include "clixon_netconf_input.h"
|
|
|
|
/* Mapping between RFC6243 withdefaults strings <--> ints
|
|
*/
|
|
static const map_str2int wdmap[] = {
|
|
{"report-all", WITHDEFAULTS_REPORT_ALL},
|
|
{"trim", WITHDEFAULTS_TRIM},
|
|
{"explicit", WITHDEFAULTS_EXPLICIT},
|
|
{"report-all-tagged", WITHDEFAULTS_REPORT_ALL_TAGGED}
|
|
};
|
|
|
|
/*! Map from with-defaults ints to strings
|
|
*
|
|
* @param[in] int Integer representation of withdefaults values
|
|
* @retval str String representation of withdefaults values
|
|
*/
|
|
char *
|
|
withdefaults_int2str(int keyword)
|
|
{
|
|
return (char*)clicon_int2str(wdmap, keyword);
|
|
}
|
|
|
|
/*! Map from with-defaults strings to ints
|
|
*
|
|
* @param[in] str String representation of withdefaults values
|
|
* @retval int Integer representation of withdefaults values
|
|
*/
|
|
int
|
|
withdefaults_str2int(char *str)
|
|
{
|
|
return clicon_str2int(wdmap, str);
|
|
}
|
|
|
|
/*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A
|
|
*
|
|
* The request requires a resource that already is in use.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_in_use(cbuf *cb,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
char *encstr = NULL;
|
|
|
|
if (cprintf(cb, "<rpc-reply xmlns=\"%s\"><rpc-error>"
|
|
"<error-type>%s</error-type>"
|
|
"<error-tag>in-use</error-tag>"
|
|
"<error-severity>error</error-severity>",
|
|
NETCONF_BASE_NAMESPACE,
|
|
type) <0)
|
|
goto err;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
|
goto err;
|
|
}
|
|
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
|
goto err;
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
err:
|
|
clixon_err(OE_XML, errno, "cprintf");
|
|
goto done;
|
|
}
|
|
|
|
/*! Create Netconf invalid-value error XML tree according to RFC 6241 Appendix A
|
|
*
|
|
* The request specifies an unacceptable value for one or more parameters.
|
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_invalid_value_xml(cxobj **xret,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval =-1;
|
|
cxobj *xerr = NULL;
|
|
char *encstr = NULL;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
|
|
"<error-type>%s</error-type>"
|
|
"<error-tag>invalid-value</error-tag>"
|
|
"<error-severity>error</error-severity>", type) < 0)
|
|
goto done;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
|
|
"<error-message>%s</error-message>", encstr) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf invalid-value error XML tree according to RFC 6241 Appendix A
|
|
*
|
|
* The request specifies an unacceptable value for one or more parameters.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_invalid_value(cbuf *cb,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_invalid_value_xml(&xret, type, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf too-big error XML tree according to RFC 6241 Appendix A
|
|
*
|
|
* The request or response (that would be generated) is
|
|
* too large for the implementation to handle.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "transport", "rpc", "application", "protocol"
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_too_big(cbuf *cb,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
char *encstr = NULL;
|
|
|
|
if (cprintf(cb, "<rpc-reply xmlns=\"%s\"><rpc-error>"
|
|
"<error-type>%s</error-type>"
|
|
"<error-tag>too-big</error-tag>"
|
|
"<error-severity>error</error-severity>",
|
|
NETCONF_BASE_NAMESPACE,
|
|
type) <0)
|
|
goto err;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
|
goto err;
|
|
}
|
|
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
|
goto err;
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
err:
|
|
clixon_err(OE_XML, errno, "cprintf");
|
|
goto done;
|
|
}
|
|
|
|
/*! Create Netconf missing-attribute error XML tree according to RFC 6241 App A
|
|
*
|
|
* An expected attribute is missing.
|
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
|
* @param[in] type Error type: "rpc", "application" or "protocol"
|
|
* @param[in] attr bad-attribute and/or bad-element xml
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_missing_attribute_xml(cxobj **xret,
|
|
char *type,
|
|
char *attr,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xerr = NULL;
|
|
char *encstr = NULL;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>%s</error-type>"
|
|
"<error-tag>missing-attribute</error-tag>"
|
|
"<error-info><bad-attribute>%s</bad-attribute></error-info>"
|
|
"<error-severity>error</error-severity>", type, attr) < 0)
|
|
goto done;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-message>%s</error-message>",
|
|
encstr) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf missing-attribute error XML tree according to RFC 6241 App A
|
|
*
|
|
* An expected attribute is missing.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "rpc", "application" or "protocol"
|
|
* @param[in] attr bad-attribute
|
|
* @param[in] message Error message (will be XML encoded) or NULL
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_missing_attribute(cbuf *cb,
|
|
char *type,
|
|
char *attr,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_missing_attribute_xml(&xret, type, attr, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf bad-attribute error XML tree according to RFC 6241 App A
|
|
*
|
|
* An attribute value is not correct; e.g., wrong type,
|
|
* out of range, pattern mismatch.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "rpc", "application" or "protocol"
|
|
* @param[in] info Attribute name
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_bad_attribute(cbuf *cb,
|
|
char *type,
|
|
char *info,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_bad_attribute_xml(&xret, type, info, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf bad-attribute error XML tree according to RFC 6241 App A
|
|
*
|
|
* An attribute value is not correct; e.g., wrong type,
|
|
* out of range, pattern mismatch.
|
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
|
* @param[in] type Error type: "rpc", "application" or "protocol"
|
|
* @param[in] info Attribute name
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
|
|
*/
|
|
int
|
|
netconf_bad_attribute_xml(cxobj **xret,
|
|
char *type,
|
|
char *info,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xerr = NULL;
|
|
char *encstr = NULL;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>%s</error-type>"
|
|
"<error-tag>bad-attribute</error-tag>"
|
|
"<error-info><bad-attribute>%s</bad-attribute></error-info>"
|
|
"<error-severity>error</error-severity>", type, info) < 0)
|
|
goto done;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-message>%s</error-message>",
|
|
encstr) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf unknown-attribute error XML tree according to RFC 6241 App A
|
|
*
|
|
* An unexpected attribute is present.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "rpc", "application" or "protocol"
|
|
* @param[in] info bad-attribute or bad-element xml
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_unknown_attribute(cbuf *cb,
|
|
char *type,
|
|
char *info,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
char *encstr = NULL;
|
|
|
|
if (cprintf(cb, "<rpc-reply xmlns=\"%s\"><rpc-error>"
|
|
"<error-type>%s</error-type>"
|
|
"<error-tag>unknown-attribute</error-tag>"
|
|
"<error-info>%s</error-info>"
|
|
"<error-severity>error</error-severity>",
|
|
NETCONF_BASE_NAMESPACE, type, info) <0)
|
|
goto err;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
|
goto err;
|
|
}
|
|
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
|
goto err;
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
err:
|
|
clixon_err(OE_XML, errno, "cprintf");
|
|
goto done;
|
|
}
|
|
|
|
/*! Common Netconf element XML tree according to RFC 6241 App A
|
|
*
|
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] tag Error tag
|
|
* @param[in] element bad-element xml
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
static int
|
|
netconf_common_xml(cxobj **xret,
|
|
char *type,
|
|
char *tag,
|
|
char *infotag,
|
|
char *element,
|
|
char *message)
|
|
{
|
|
int retval =-1;
|
|
cxobj *xerr;
|
|
char *encstr = NULL;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>%s</error-type>"
|
|
"<error-tag>%s</error-tag>"
|
|
"<error-info><%s>%s</%s></error-info>"
|
|
"<error-severity>error</error-severity>",
|
|
type, tag, infotag, element, infotag) < 0)
|
|
goto done;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-message>%s</error-message>",
|
|
encstr) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf missing-element error XML tree according to RFC 6241 App A
|
|
*
|
|
* An expected element is missing.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] info bad-element xml
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_missing_element(cbuf *cb,
|
|
char *type,
|
|
char *element,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_common_xml(&xret, type, "missing-element",
|
|
"bad-element", element, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf missing-element error XML tree according to RFC 6241 App A
|
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] element bad-element xml
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_missing_element_xml(cxobj **xret,
|
|
char *type,
|
|
char *element,
|
|
char *message)
|
|
{
|
|
return netconf_common_xml(xret, type, "missing-element",
|
|
"bad-element", element, message);
|
|
}
|
|
|
|
/*! Create Netconf bad-element error XML tree according to RFC 6241 App A
|
|
*
|
|
* An element value is not correct; e.g., wrong type, out of range,
|
|
* pattern mismatch.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] elemnt Bad element name
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_bad_element(cbuf *cb,
|
|
char *type,
|
|
char *element,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_common_xml(&xret, type, "bad-element",
|
|
"bad-element",element, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
int
|
|
netconf_bad_element_xml(cxobj **xret,
|
|
char *type,
|
|
char *element,
|
|
char *message)
|
|
{
|
|
return netconf_common_xml(xret, type, "bad-element", "bad-element", element, message);
|
|
}
|
|
|
|
/*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
|
|
*
|
|
* An unexpected element is present.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] element Bad element name
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_unknown_element(cbuf *cb,
|
|
char *type,
|
|
char *element,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_common_xml(&xret, type, "unknown-element",
|
|
"bad-element", element, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf unknown-element error XML tree according to RFC 6241 App A
|
|
*
|
|
* An unexpected element is present.
|
|
* @param[out] xret XML buffer
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] element Bad element name
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_unknown_element_xml(cxobj **xret,
|
|
char *type,
|
|
char *element,
|
|
char *message)
|
|
{
|
|
return netconf_common_xml(xret, type, "unknown-element",
|
|
"bad-element", element, message);
|
|
}
|
|
|
|
/*! Create Netconf unknown-namespace error XML tree according to RFC 6241 App A
|
|
*
|
|
* An unexpected namespace is present.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] info bad-element or bad-namespace xml
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_unknown_namespace(cbuf *cb,
|
|
char *type,
|
|
char *ns,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_common_xml(&xret, type, "unknown-namespace",
|
|
"bad-namespace", ns, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
int
|
|
netconf_unknown_namespace_xml(cxobj **xret,
|
|
char *type,
|
|
char *ns,
|
|
char *message)
|
|
{
|
|
return netconf_common_xml(xret, type, "unknown-namespace",
|
|
"bad-namespace", ns, message);
|
|
}
|
|
|
|
/*! Create Netconf access-denied error cbuf according to RFC 6241 App A
|
|
*
|
|
* Access to the requested protocol operation or data model is denied because
|
|
* authorization failed.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @see netconf_access_denied_xml Same but returns XML tree
|
|
*/
|
|
int
|
|
netconf_access_denied(cbuf *cb,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_access_denied_xml(&xret, type, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf access-denied error XML tree according to RFC 6241 App A
|
|
*
|
|
* Access to the requested protocol operation or data model is denied because
|
|
* authorization failed.
|
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @code
|
|
* cxobj *xret = NULL;
|
|
* if (netconf_access_denied_xml(&xret, "protocol", "Unauthorized") < 0)
|
|
* err;
|
|
* xml_free(xret);
|
|
* @endcode
|
|
* @see netconf_access_denied Same but returns cligen buffer
|
|
*/
|
|
int
|
|
netconf_access_denied_xml(cxobj **xret,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval =-1;
|
|
cxobj *xerr;
|
|
char *encstr = NULL;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>%s</error-type>"
|
|
"<error-tag>access-denied</error-tag>"
|
|
"<error-severity>error</error-severity>", type) < 0)
|
|
goto done;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-message>%s</error-message>",
|
|
encstr) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf lock-denied error XML tree according to RFC 6241 App A
|
|
*
|
|
* Access to the requested lock is denied because the lock is currently held
|
|
* by another entity.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] info session-id xml
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_lock_denied(cbuf *cb,
|
|
char *info,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
char *encstr = NULL;
|
|
|
|
if (cprintf(cb, "<rpc-reply xmlns=\"%s\"><rpc-error>"
|
|
"<error-type>protocol</error-type>"
|
|
"<error-tag>lock-denied</error-tag>"
|
|
"<error-info>%s</error-info>"
|
|
"<error-severity>error</error-severity>",
|
|
NETCONF_BASE_NAMESPACE, info) <0)
|
|
goto err;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
|
goto err;
|
|
}
|
|
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
|
goto err;
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
err:
|
|
clixon_err(OE_XML, errno, "cprintf");
|
|
goto done;
|
|
}
|
|
|
|
/*! Create Netconf resource-denied error XML tree according to RFC 6241 App A
|
|
*
|
|
* Request could not be completed because of insufficient resources.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "transport, "rpc", "application", "protocol"
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_resource_denied(cbuf *cb,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
char *encstr = NULL;
|
|
|
|
if (cprintf(cb, "<rpc-reply xmlns=\"%s\"><rpc-error>"
|
|
"<error-type>%s</error-type>"
|
|
"<error-tag>resource-denied</error-tag>"
|
|
"<error-severity>error</error-severity>",
|
|
NETCONF_BASE_NAMESPACE, type) <0)
|
|
goto err;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
|
goto err;
|
|
}
|
|
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
|
goto err;
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
err:
|
|
clixon_err(OE_XML, errno, "cprintf");
|
|
goto done;
|
|
}
|
|
|
|
/*! Create Netconf rollback-failed error XML tree according to RFC 6241 App A
|
|
*
|
|
* Request to roll back some configuration change (via rollback-on-error or
|
|
* <discard-changes> operations) was not completed for some reason.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_rollback_failed(cbuf *cb,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
char *encstr = NULL;
|
|
|
|
if (cprintf(cb, "<rpc-reply xmlns=\"%s\"><rpc-error>"
|
|
"<error-type>%s</error-type>"
|
|
"<error-tag>rollback-failed</error-tag>"
|
|
"<error-severity>error</error-severity>",
|
|
NETCONF_BASE_NAMESPACE, type) <0)
|
|
goto err;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
|
goto err;
|
|
}
|
|
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
|
goto err;
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
err:
|
|
clixon_err(OE_XML, errno, "cprintf");
|
|
goto done;
|
|
}
|
|
|
|
/*! Create Netconf data-exists error XML tree according to RFC 6241 Appendix A
|
|
*
|
|
* Request could not be completed because the relevant
|
|
* data model content already exists. For example,
|
|
* a "create" operation was attempted on data that already exists.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_data_exists(cbuf *cb,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
char *encstr = NULL;
|
|
|
|
if (cprintf(cb, "<rpc-reply xmlns=\"%s\"><rpc-error>"
|
|
"<error-type>application</error-type>"
|
|
"<error-tag>data-exists</error-tag>"
|
|
"<error-severity>error</error-severity>",
|
|
NETCONF_BASE_NAMESPACE) <0)
|
|
goto err;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
|
goto err;
|
|
}
|
|
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
|
goto err;
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
err:
|
|
clixon_err(OE_XML, errno, "cprintf");
|
|
goto done;
|
|
}
|
|
|
|
/*! Create Netconf data-missing error XML tree according to RFC 6241 App A
|
|
*
|
|
* Request could not be completed because the relevant data model content
|
|
* does not exist. For example, a "delete" operation was attempted on
|
|
* data that does not exist.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_data_missing(cbuf *cb,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_data_missing_xml(&xret, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf data-missing error XML tree according to RFC 6241 App A / RFC 7950 15.6(choice)
|
|
*
|
|
* Request could not be completed because the relevant data model content
|
|
* does not exist. For example, a "delete" operation was attempted on
|
|
* data that does not exist.
|
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_data_missing_xml(cxobj **xret,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
char *encstr = NULL;
|
|
cxobj *xerr;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
|
|
"<error-type>application</error-type>"
|
|
"<error-tag>data-missing</error-tag>") < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
|
|
"<error-severity>error</error-severity>") < 0)
|
|
goto done;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
|
|
"<error-message>%s</error-message>", encstr) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf data-missing / missing-choice as defeind in RFC 7950 15.6
|
|
*
|
|
* If a NETCONF operation would result in configuration data where no
|
|
* nodes exists in a mandatory choice, the following error MUST be
|
|
* returned:
|
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
|
* @param[in] x Element with missing choice
|
|
* @param[in] name Name of missing mandatory choice
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_missing_choice_xml(cxobj **xret,
|
|
cxobj *x,
|
|
char *name,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
char *encstr = NULL;
|
|
cxobj *xerr;
|
|
char *path = NULL;
|
|
char *encpath = NULL;
|
|
|
|
if (xret == NULL || name == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret or name is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
/* error-path: Path to the element with the missing choice. */
|
|
if (xml2xpath(x, NULL, 0, 0, &path) < 0)
|
|
goto done;
|
|
if (xml_chardata_encode(&encpath, 0, "%s", path) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
|
|
"<error-type>application</error-type>"
|
|
"<error-tag>data-missing</error-tag>"
|
|
"<error-app-tag>missing-choice</error-app-tag>"
|
|
"<error-path>%s</error-path>"
|
|
"<error-info><missing-choice xmlns=\"%s\">%s</missing-choice></error-info>"
|
|
"<error-severity>error</error-severity>",
|
|
encpath,
|
|
YANG_XML_NAMESPACE,
|
|
name) < 0)
|
|
goto done;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
|
|
"<error-message>%s</error-message>", encstr) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (path)
|
|
free(path);
|
|
if (encstr)
|
|
free(encstr);
|
|
if (encpath)
|
|
free(encpath);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf operation-not-supported error XML according to RFC 6241 App A
|
|
*
|
|
* Request could not be completed because the requested operation is not
|
|
* supported by this implementation.
|
|
* @param[out] xret Error XML tree
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @code
|
|
* cxobj *xret = NULL;
|
|
* if (netconf_operation_not_supported_xml(&xret, "protocol", "Unauthorized") < 0)
|
|
* err;
|
|
* xml_free(xret);
|
|
* @endcode
|
|
* @see netconf_operation_not_supported Same but returns cligen buffer
|
|
*/
|
|
int
|
|
netconf_operation_not_supported_xml(cxobj **xret,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval =-1;
|
|
cxobj *xerr;
|
|
char *encstr = NULL;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>%s</error-type>"
|
|
"<error-tag>operation-not-supported</error-tag>"
|
|
"<error-severity>error</error-severity>",
|
|
type) < 0)
|
|
goto done;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-message>%s</error-message>",
|
|
encstr) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf operation-not-supported error XML according to RFC 6241 App A
|
|
*
|
|
* Request could not be completed because the requested operation is not
|
|
* supported by this implementation.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "application" or "protocol"
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
netconf_operation_not_supported(cbuf *cb,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_operation_not_supported_xml(&xret, type, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf operation-failed error XML tree according to RFC 6241 App A
|
|
*
|
|
* Request could not be completed because the requested operation failed for
|
|
* some reason not covered by any other error condition.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] type Error type: "rpc", "application" or "protocol"
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @see netconf_operation_failed_xml Same but returns XML tree
|
|
*/
|
|
int
|
|
netconf_operation_failed(cbuf *cb,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_operation_failed_xml(&xret, type, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf operation-failed error XML tree according to RFC 6241 App A
|
|
*
|
|
* Request could not be completed because the requested operation failed for
|
|
* some reason not covered by any other error condition.
|
|
* @param[out] xret Error XML tree
|
|
* @param[in] type Error type: "rpc", "application" or "protocol"
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @code
|
|
* cxobj *xret = NULL;
|
|
* if (netconf_operation_failed_xml(&xret, "protocol", "Unauthorized") < 0)
|
|
* err;
|
|
* xml_free(xret);
|
|
* @endcode
|
|
* @see netconf_operation_failed Same but returns cligen buffer
|
|
*/
|
|
int
|
|
netconf_operation_failed_xml(cxobj **xret,
|
|
char *type,
|
|
char *message)
|
|
{
|
|
int retval =-1;
|
|
cxobj *xerr;
|
|
char *encstr = NULL;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>%s</error-type>"
|
|
"<error-tag>operation-failed</error-tag>"
|
|
"<error-severity>error</error-severity>",
|
|
type) < 0)
|
|
goto done;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-message>%s</error-message>",
|
|
encstr) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf malformed-message error cbuf according to RFC 6241 App A
|
|
*
|
|
* A message could not be handled because it failed to be parsed correctly.
|
|
* For example, the message is not well-formed XML or it uses an
|
|
* invalid character set.
|
|
* @param[out] cb CLIgen buf. Error XML is written in this buffer
|
|
* @param[in] message Error message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @note New in :base:1.1
|
|
* @see netconf_malformed_message_xml Same but returns XML tree
|
|
*/
|
|
int
|
|
netconf_malformed_message(cbuf *cb,
|
|
char *message)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_malformed_message_xml(&xret, message) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf malformed-message error XML tree according to RFC 6241 App A
|
|
*
|
|
* A message could not be handled because it failed to be parsed correctly.
|
|
* For example, the message is not well-formed XML or it uses an
|
|
* invalid character set.
|
|
* @param[out] xret Error XML tree
|
|
* @param[in] message Error message (will be XML encoded)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @note New in :base:1.1
|
|
* @code
|
|
* cxobj *xret = NULL;
|
|
* if (netconf_malformed_message_xml(&xret, "Unauthorized") < 0)
|
|
* err;
|
|
* xml_free(xret);
|
|
* @endcode
|
|
* @see netconf_malformed_message Same but returns cligen buffer
|
|
*/
|
|
int
|
|
netconf_malformed_message_xml(cxobj **xret,
|
|
char *message)
|
|
{
|
|
int retval =-1;
|
|
cxobj *xerr;
|
|
char *encstr = NULL;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>rpc</error-type>"
|
|
"<error-tag>malformed-message</error-tag>"
|
|
"<error-severity>error</error-severity>") < 0)
|
|
goto done;
|
|
if (message){
|
|
if (xml_chardata_encode(&encstr, 0, "%s", message) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-message>%s</error-message>",
|
|
encstr) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf data-not-unique error message according to RFC 7950 15.1
|
|
*
|
|
* A NETCONF operation would result in configuration data where a
|
|
* "unique" constraint is invalidated.
|
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
|
* @param[in] x List element containing duplicate
|
|
* @param[in] cvk List of components in x that are non-unique
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @see RFC7950 Sec 15.1
|
|
* @see netconf_data_not_unique_xml Same but returns XML tree
|
|
*/
|
|
int
|
|
netconf_data_not_unique(cbuf *cb,
|
|
cxobj *x,
|
|
cvec *cvk)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xret = NULL;
|
|
|
|
if (netconf_data_not_unique_xml(&xret, x, cvk) < 0)
|
|
goto done;
|
|
if (clixon_xml2cbuf(cb, xret, 0, 0, NULL, -1, 0) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (xret)
|
|
xml_free(xret);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf data-not-unique error message according to RFC 7950 15.1
|
|
*
|
|
* A NETCONF operation would result in configuration data where a
|
|
* "unique" constraint is invalidated.
|
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
|
* @param[in] x List element containing duplicate
|
|
* @param[in] cvk List of components in x that are non-unique
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @see RFC7950 Sec 15.1
|
|
*/
|
|
int
|
|
netconf_data_not_unique_xml(cxobj **xret,
|
|
cxobj *x,
|
|
cvec *cvk)
|
|
{
|
|
int retval = -1;
|
|
cg_var *cvi = NULL;
|
|
cxobj *xerr;
|
|
cxobj *xinfo = NULL;
|
|
char *path = NULL;
|
|
char *encpath = NULL;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL,
|
|
"<error-type>application</error-type>"
|
|
"<error-tag>operation-failed</error-tag>"
|
|
"<error-app-tag>data-not-unique</error-app-tag>"
|
|
"<error-severity>error</error-severity>") < 0)
|
|
goto done;
|
|
/* error-info: <non-unique> Contains an instance identifier that points to a leaf
|
|
* that invalidates the "unique" constraint. This element is present once for each
|
|
* non-unique leaf. */
|
|
if (cvk && cvec_len(cvk)){
|
|
if ((xinfo = xml_new("error-info", xerr, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (xml2xpath(x, NULL, 0, 0, &path) < 0)
|
|
goto done;
|
|
if (xml_chardata_encode(&encpath, 0, "%s", path) < 0)
|
|
goto done;
|
|
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xinfo, NULL,
|
|
"<non-unique xmlns=\"%s\">%s/%s</non-unique>",
|
|
YANG_XML_NAMESPACE,
|
|
encpath,
|
|
cv_string_get(cvi)) < 0)
|
|
goto done;
|
|
}
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (path)
|
|
free(path);
|
|
if (encpath)
|
|
free(encpath);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf too-many/few-elements err msg according to RFC 7950 15.2/15.3
|
|
*
|
|
* A NETCONF operation would result in configuration data where a
|
|
* list or a leaf-list would have too many entries, the following error
|
|
* @param[out] xret Error XML tree. Free with xml_free after use
|
|
* @param[in] xp XML parent node (for error)
|
|
* @param[in] name Name of list (for error)
|
|
* @param[in] max If set, return too-many, otherwise too-few
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @see RFC7950 Sec 15.1
|
|
*/
|
|
int
|
|
netconf_minmax_elements_xml(cxobj **xret,
|
|
cxobj *xp,
|
|
char *name,
|
|
int max)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xerr;
|
|
char *path = NULL;
|
|
char *encpath = NULL;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
}
|
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
|
goto done;
|
|
if (xml_add_attr(*xret, "xmlns", NETCONF_BASE_NAMESPACE, NULL, NULL) == NULL)
|
|
goto done;
|
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
|
goto done;
|
|
if (xml_parent(xp)){ /* Dont include root, eg <config> */
|
|
if (xml2xpath(xp, NULL, 0, 0, &path) < 0)
|
|
goto done;
|
|
if (xml_chardata_encode(&encpath, 0, "%s", path) < 0)
|
|
goto done;
|
|
}
|
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>protocol</error-type>"
|
|
"<error-tag>operation-failed</error-tag>"
|
|
"<error-app-tag>too-%s-elements</error-app-tag>"
|
|
"<error-severity>error</error-severity>"
|
|
"<error-path>%s/%s</error-path>",
|
|
max?"many":"few",
|
|
encpath?encpath:"",
|
|
name) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (path)
|
|
free(path);
|
|
if (encpath)
|
|
free(encpath);
|
|
return retval;
|
|
}
|
|
|
|
/*! Help function: merge - check yang - if error make netconf errmsg
|
|
*
|
|
* @param[in] x XML tree
|
|
* @param[in] yspec Yang spec
|
|
* @param[in,out] xret Existing XML tree, merge x into this
|
|
* @retval 1 OK
|
|
* @retval 0 Statedata callback failed, error in xret?
|
|
* @retval -1 Error (fatal)
|
|
*/
|
|
int
|
|
netconf_trymerge(cxobj *x,
|
|
yang_stmt *yspec,
|
|
cxobj **xret)
|
|
{
|
|
int retval = -1;
|
|
char *reason = NULL;
|
|
cxobj *xc;
|
|
|
|
if (xret == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "xret is NULL");
|
|
goto done;
|
|
}
|
|
if (*xret == NULL){
|
|
if ((*xret = xml_dup(x)) == NULL)
|
|
goto done;
|
|
goto ok;
|
|
}
|
|
if (xml_merge(*xret, x, yspec, &reason) < 0)
|
|
goto done;
|
|
if (reason){
|
|
while ((xc = xml_child_i(*xret, 0)) != NULL)
|
|
xml_purge(xc);
|
|
if (netconf_operation_failed_xml(xret, "rpc", reason)< 0)
|
|
goto done;
|
|
goto fail;
|
|
}
|
|
ok:
|
|
retval = 1;
|
|
done:
|
|
if (reason)
|
|
free(reason);
|
|
return retval;
|
|
fail:
|
|
retval = 0;
|
|
goto done;
|
|
}
|
|
|
|
/*! Load ietf netconf yang module and set enabled features
|
|
*
|
|
* This function should be called after options loaded but before yang modules.
|
|
* (a yang module may import ietf-netconf and then features must be set)
|
|
* @param[in] h Clixon handle
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* The features added are (in order) (numbers are section# in RFC6241):
|
|
* candidate (8.3)
|
|
* validate (8.6)
|
|
* startup (8.7)
|
|
* xpath (8.9)
|
|
* @see netconf_module_load that is called later
|
|
*/
|
|
int
|
|
netconf_module_features(clixon_handle h)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xc;
|
|
|
|
if ((xc = clicon_conf_xml(h)) == NULL){
|
|
clixon_err(OE_CFG, ENOENT, "Clicon configuration not loaded");
|
|
goto done;
|
|
}
|
|
/* Enable features (hardcoded here) */
|
|
if (clixon_xml_parse_string("<CLICON_FEATURE>ietf-netconf:candidate</CLICON_FEATURE>",
|
|
YB_PARENT, NULL, &xc, NULL) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_string("<CLICON_FEATURE>ietf-netconf:validate</CLICON_FEATURE>",
|
|
YB_PARENT, NULL, &xc, NULL) < 0)
|
|
goto done;
|
|
if (clixon_xml_parse_string("<CLICON_FEATURE>ietf-netconf:xpath</CLICON_FEATURE>",
|
|
YB_PARENT, NULL, &xc, NULL) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Load generic yang specs, ie ietf netconf yang module and set enabled features
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @see netconf_module_feature should be called before any yang modules
|
|
*/
|
|
int
|
|
netconf_module_load(clixon_handle h)
|
|
{
|
|
int retval = -1;
|
|
yang_stmt *yspec;
|
|
|
|
yspec = clicon_dbspec_yang(h);
|
|
/* Load yang spec */
|
|
if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec)< 0)
|
|
goto done;
|
|
/* Load yang Netconf stream discovery
|
|
* Actually add by default since create-subscription is in this module
|
|
*/
|
|
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277"))
|
|
if (yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
|
|
goto done;
|
|
/* Load yang Restconf stream discovery */
|
|
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
|
|
yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
|
|
goto done;
|
|
/* YANG module revision change management */
|
|
if (clicon_option_bool(h, "CLICON_XML_CHANGELOG"))
|
|
if (yang_spec_parse_module(h, "clixon-xml-changelog", NULL, yspec)< 0)
|
|
goto done;
|
|
/* Load restconf yang to data. Note clixon-restconf.yang is always part of clixon-config */
|
|
if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS"))
|
|
if (yang_spec_parse_module(h, "clixon-restconf", NULL, yspec)< 0)
|
|
goto done;
|
|
/* XXX: Both the following settings are because clicon-handle is not part of all API
|
|
* functions
|
|
* Treat unknown XML as anydata */
|
|
if (clicon_option_bool(h, "CLICON_YANG_UNKNOWN_ANYDATA") == 1)
|
|
xml_bind_yang_unknown_anydata(1);
|
|
/* Make message-id attribute optional */
|
|
if (clicon_option_bool(h, "CLICON_NETCONF_MESSAGE_ID_OPTIONAL") == 1)
|
|
xml_bind_netconf_message_id_optional(1);
|
|
/* Load ietf list pagination */
|
|
if (yang_spec_parse_module(h, "ietf-list-pagination", NULL, yspec)< 0)
|
|
goto done;
|
|
/* Load ietf list pagination netconf */
|
|
if (yang_spec_parse_module(h, "ietf-list-pagination-nc", NULL, yspec)< 0)
|
|
goto done;
|
|
/* RFC6243 With-defaults Capability for NETCONF */
|
|
if (yang_spec_parse_module(h, "ietf-netconf-with-defaults", NULL, yspec)< 0)
|
|
goto done;
|
|
/* RFC6022 YANG Module for NETCONF Monitoring */
|
|
if (yang_spec_parse_module(h, "ietf-netconf-monitoring", NULL, yspec)< 0)
|
|
goto done;
|
|
/* Framing: If hello protocol skipped, set framing direct, ie fix chunked framing if NETCONF-1.1
|
|
* But start with default: RFC 4741 EOM ]]>]]>
|
|
* For now this only applies to external protocol
|
|
*/
|
|
clicon_data_int_set(h, NETCONF_FRAMING_TYPE, NETCONF_SSH_EOM);
|
|
if (clicon_option_bool(h, "CLICON_NETCONF_HELLO_OPTIONAL")){
|
|
if (clicon_option_int(h, "CLICON_NETCONF_BASE_CAPABILITY") > 0) /* RFC 6241 */
|
|
clicon_data_int_set(h, NETCONF_FRAMING_TYPE, NETCONF_SSH_CHUNKED);
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Find some sub-child in netconf/xm request.
|
|
*
|
|
* Actually, find a child with a certain name and return its body
|
|
* @param[in] xn
|
|
* @param[in] name
|
|
* @retval db Name of database
|
|
* @retval NULL Not found
|
|
* The following code returns "source"
|
|
* @code
|
|
* cxobj *xt = NULL;
|
|
* char *db;
|
|
* clixon_xml_parse_string("<x><target>source</target></x>", YB_NONE, NULL, &xt, NULL);
|
|
* db = netconf_db_find(xt, "target");
|
|
* @endcode
|
|
*/
|
|
char*
|
|
netconf_db_find(cxobj *xn,
|
|
char *name)
|
|
{
|
|
cxobj *xs; /* source */
|
|
cxobj *xi;
|
|
char *db = NULL;
|
|
|
|
/* XXX should use prefix cf edit_config */
|
|
if ((xs = xml_find(xn, name)) == NULL)
|
|
goto done;
|
|
if ((xi = xml_child_i(xs, 0)) == NULL)
|
|
goto done;
|
|
db = xml_name(xi);
|
|
done:
|
|
return db;
|
|
}
|
|
|
|
/* See RFC 8040 4.8.1
|
|
* @see netconf_content_str2int
|
|
*/
|
|
static const map_str2int netconf_content_map[] = {
|
|
{"config", CONTENT_CONFIG},
|
|
{"nonconfig", CONTENT_NONCONFIG},
|
|
{"all", CONTENT_ALL},
|
|
{NULL, -1}
|
|
};
|
|
|
|
const netconf_content
|
|
netconf_content_str2int(char *str)
|
|
{
|
|
return clicon_str2int(netconf_content_map, str);
|
|
}
|
|
|
|
const char *
|
|
netconf_content_int2str(netconf_content nr)
|
|
{
|
|
return clicon_int2str(netconf_content_map, nr);
|
|
}
|
|
|
|
/*! List capabilities
|
|
*
|
|
* 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 hello 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)
|
|
*/
|
|
int
|
|
netconf_capabilites(clixon_handle h,
|
|
cbuf *cb)
|
|
{
|
|
int retval = -1;
|
|
char *encstr = NULL;
|
|
char *ietf_yang_library_revision;
|
|
yang_stmt *yspec = clicon_dbspec_yang(h);
|
|
char *module_set_id;
|
|
|
|
module_set_id = clicon_option_str(h, "CLICON_MODULE_SET_ID");
|
|
cprintf(cb, "<capabilities>");
|
|
if (clicon_option_int(h, "CLICON_NETCONF_BASE_CAPABILITY") > 0){
|
|
/* 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, 0, "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);
|
|
if (encstr){
|
|
free(encstr);
|
|
encstr = NULL;
|
|
}
|
|
}
|
|
/* RFC6241 Sec 8.3. Candidate Configuration Capability */
|
|
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>");
|
|
/* RFC6241 Sec 8.6. Validate Capability */
|
|
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:validate:1.1</capability>");
|
|
/* rfc 6241 Sec 8.7 Distinct Startup Capability */
|
|
if (if_feature(yspec, "ietf-netconf", "startup"))
|
|
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:startup:1.0</capability>");
|
|
/* RFC6241 Sec 8.9. XPath Capability */
|
|
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:xpath:1.0</capability>");
|
|
/* rfc6243 with-defaults capability modes */
|
|
cprintf(cb, "<capability>");
|
|
xml_chardata_cbuf_append(cb, 0, "urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all,trim,report-all-tagged");
|
|
cprintf(cb, "</capability>");
|
|
/* RFC5277 Notification Capability */
|
|
cprintf(cb, "<capability>%s</capability>", NETCONF_NOTIFICATION_CAPABILITY);
|
|
/* RFC6022 YANG Module for NETCONF Monitoring
|
|
* This seems non-standard but necessary for most existing boxes and software
|
|
*/
|
|
if (clicon_option_bool(h, "CLICON_NETCONF_MONITORING"))
|
|
cprintf(cb, "<capability>%s</capability>", NETCONF_MONITORING_NAMESPACE);
|
|
|
|
/* It is somewhat arbitrary why some features/capabilities are hardocded and why some are not
|
|
* rfc 6241 Sec 8.4 confirmed-commit capabilities */
|
|
if (if_feature(yspec, "ietf-netconf", "confirmed-commit"))
|
|
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:confirmed-commit:1.1</capability>");
|
|
cprintf(cb, "</capabilities>");
|
|
retval = 0;
|
|
done:
|
|
if (encstr)
|
|
free(encstr);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create Netconf server hello. Single cap and defer individual to querying modules
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @param[in] cb Msg buffer
|
|
* @param[in] session_id Id of client session
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* 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
|
|
*
|
|
* @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 If you add new, remember to encode bodies if needed, see xml_chardata_encode()
|
|
* @note
|
|
* @see yang_modules_state_get
|
|
* @see netconf_module_load
|
|
*/
|
|
int
|
|
netconf_hello_server(clixon_handle h,
|
|
cbuf *cb,
|
|
uint32_t session_id)
|
|
{
|
|
int retval = -1;
|
|
|
|
cprintf(cb, "<hello xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
|
|
if (netconf_capabilites(h, cb) < 0)
|
|
goto done;
|
|
if (session_id)
|
|
cprintf(cb, "<session-id>%lu</session-id>", (long unsigned int)session_id);
|
|
cprintf(cb, "</hello>");
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*! Add internal error info to existing netconf error message by rewriting
|
|
*
|
|
* If a eg sanity check detects errors in internal messages passing, such as from a plugin or
|
|
* in backend communication, an error is generated. However, it does not make sense to send this
|
|
* error message as-is to the requestor. Instead this function transforms the error to a more
|
|
* generic "operation-failed" error and adds info in its error message to say it is an internal error.
|
|
* If a requestor receives such an error, it will be clear that the error is internal.
|
|
* @param[in] xerr Netconf error xml tree on the form: <rpc-error>
|
|
* @param[in] msg Error message
|
|
* @param[in] arg Extra error message (consider stdarg?)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
*/
|
|
int
|
|
clixon_netconf_internal_error(cxobj *xerr,
|
|
char *msg,
|
|
char *arg)
|
|
{
|
|
int retval = -1;
|
|
cxobj *xr;
|
|
cxobj *xb;
|
|
|
|
if ((xr = xpath_first(xerr, NULL, "//error-tag")) != NULL &&
|
|
(xb = xml_body_get(xr))){
|
|
if (xml_value_set(xb, "operation-failed") < 0)
|
|
goto done;
|
|
}
|
|
if ((xr = xpath_first(xerr, NULL, "//error-message")) != NULL &&
|
|
(xb = xml_body_get(xr))){
|
|
if (xml_value_append(xb, msg) < 0)
|
|
goto done;
|
|
if (arg &&xml_value_append(xb, arg) < 0)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Parse string into uint32 and return netconf bad-element msg on error
|
|
*
|
|
* @param[in] name Name of attribute (for error msg)
|
|
* @param[in] valstr Value string to be parsed
|
|
* @param[in] defaultstr If given, default string which is accepted and sets value to 0
|
|
* @param[in,out] cbret Output buffer for internal RPC message if invalid
|
|
* @param[out] value Value if valid
|
|
* @retval 1 OK
|
|
* @retval 0 Invalid, cbret set
|
|
* @retval -1 Error
|
|
* @code
|
|
* char *name = "a";
|
|
* char *valstr = "322";
|
|
* char *defaultstr = "unbounded";
|
|
* cbuf *cbret = cbuf_new();
|
|
* uint32_t value = 0;
|
|
*
|
|
* if ((ret = netconf_parse_uint32(name, valstr, defaultstr, cbret, &value) < 0)
|
|
* goto err;
|
|
* if (ret == 0)
|
|
* // cbret contains netconf errmsg
|
|
* else
|
|
* // value contains uin32
|
|
* @endcode
|
|
*/
|
|
int
|
|
netconf_parse_uint32(char *name,
|
|
char *valstr,
|
|
char *defaultstr,
|
|
uint32_t defaultvalue,
|
|
cbuf *cbret,
|
|
uint32_t *value)
|
|
{
|
|
int retval = -1;
|
|
int ret;
|
|
char *reason = NULL;
|
|
|
|
if (valstr == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "valstr is NULL");
|
|
goto done;
|
|
}
|
|
if (defaultstr && strcmp(valstr, defaultstr) == 0)
|
|
*value = defaultvalue;
|
|
else {
|
|
if ((ret = parse_uint32(valstr, value, &reason)) < 0){
|
|
clixon_err(OE_XML, errno, "parse_uint32");
|
|
goto done;
|
|
}
|
|
if (ret == 0){
|
|
if (netconf_bad_element(cbret, "application",
|
|
name, "Unrecognized value") < 0)
|
|
goto done;
|
|
goto fail;
|
|
}
|
|
}
|
|
retval = 1;
|
|
done:
|
|
if (reason)
|
|
free(reason);
|
|
return retval;
|
|
fail:
|
|
retval = 0;
|
|
goto done;
|
|
}
|
|
|
|
/*! Parse string into uint32 and return netconf bad-element msg on error xml variant
|
|
*
|
|
* @see netconf_parse_uint32_xml
|
|
*/
|
|
int
|
|
netconf_parse_uint32_xml(char *name,
|
|
char *valstr,
|
|
char *defaultstr,
|
|
uint32_t defaultvalue,
|
|
cxobj **xerr,
|
|
uint32_t *value)
|
|
{
|
|
int retval = -1;
|
|
int ret;
|
|
char *reason = NULL;
|
|
|
|
if (valstr == NULL){
|
|
clixon_err(OE_NETCONF, EINVAL, "valstr is NULL");
|
|
goto done;
|
|
}
|
|
if (defaultstr && strcmp(valstr, defaultstr) == 0)
|
|
*value = defaultvalue;
|
|
else {
|
|
if ((ret = parse_uint32(valstr, value, &reason)) < 0){
|
|
clixon_err(OE_XML, errno, "parse_uint32");
|
|
goto done;
|
|
}
|
|
if (ret == 0){
|
|
if (netconf_bad_element_xml(xerr, "application",
|
|
name, "Unrecognized value") < 0)
|
|
goto done;
|
|
goto fail;
|
|
}
|
|
}
|
|
retval = 1;
|
|
done:
|
|
if (reason)
|
|
free(reason);
|
|
return retval;
|
|
fail:
|
|
retval = 0;
|
|
goto done;
|
|
}
|
|
|
|
/*! Get next netconf rpc message-id
|
|
*
|
|
* @param[in] h Clixon handle
|
|
* @retval msgid Next message id
|
|
*/
|
|
int
|
|
netconf_message_id_next(clixon_handle h)
|
|
{
|
|
int msgid;
|
|
|
|
if ((msgid=clicon_option_int(h, "netconf-message-id")) < 0){
|
|
clicon_option_str_set(h, "netconf-message-id", NETCONF_MESSAGE_ID_DEFAULT);
|
|
msgid = clicon_option_int(h, "netconf-message-id");
|
|
}
|
|
else {
|
|
msgid++;
|
|
msgid %= 0x7ffffff; /* Wrap at some number */
|
|
clicon_option_int_set(h, "netconf-message-id", msgid);
|
|
}
|
|
return msgid;
|
|
}
|
|
|
|
/*! Add netconf xml postamble of message. I.e, xml after the body of the message.
|
|
*
|
|
* @param[in] framing Netconf framing
|
|
* @param[in,out] cb Netconf packet (cligen buffer)
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* XXX: copies body
|
|
*/
|
|
int
|
|
netconf_framing_preamble(netconf_framing_type framing,
|
|
cbuf *cb)
|
|
{
|
|
int retval = -1;
|
|
char *body = NULL;
|
|
|
|
switch (framing){
|
|
case NETCONF_SSH_EOM:
|
|
break;
|
|
case NETCONF_SSH_CHUNKED:
|
|
if ((body = strdup(cbuf_get(cb))) == NULL){
|
|
clixon_err(OE_UNIX, errno, "strdup");
|
|
goto done;
|
|
}
|
|
cbuf_reset(cb);
|
|
cprintf(cb, "\n#%zu\n", strlen(body)); /* Add RFC6242 chunked-end */
|
|
cbuf_append_str(cb, body);
|
|
break;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (body)
|
|
free(body);
|
|
return retval;
|
|
}
|
|
|
|
/*! Add netconf xml postamble of message. I.e, xml after the body of the message.
|
|
*
|
|
* @param[in] framing Netconf framing
|
|
* @param[in,out] cb Netconf packet (cligen buffer)
|
|
*/
|
|
int
|
|
netconf_framing_postamble(netconf_framing_type framing,
|
|
cbuf *cb)
|
|
{
|
|
switch (framing){
|
|
case NETCONF_SSH_EOM:
|
|
cprintf(cb, "]]>]]>"); /* Add RFC4742 end-of-message marker */
|
|
break;
|
|
case NETCONF_SSH_CHUNKED:
|
|
cprintf(cb, "\n##\n"); /* Add RFC6242 chunked-end */
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*! 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
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @see netconf_output_encap for function with encapsulation
|
|
*/
|
|
int
|
|
netconf_output(int s,
|
|
cbuf *cb,
|
|
char *msg)
|
|
{
|
|
int retval = -1;
|
|
char *buf = cbuf_get(cb);
|
|
int len = cbuf_len(cb);
|
|
|
|
clixon_debug(CLIXON_DBG_MSG, "Send ext: %s", cbuf_get(cb));
|
|
#if 0 // Extra sanity check for debugging
|
|
{
|
|
cxobj *xt = NULL;
|
|
if (clixon_xml_parse_string(buf, YB_NONE, NULL, &xt, NULL) == 0){
|
|
if (clixon_xml2file(stderr, xml_child_i(xt, 0), 0, 0, NULL, fprintf, 0, 0) < 0)
|
|
goto done;
|
|
fprintf(stderr, "\n");
|
|
xml_free(xt);
|
|
}
|
|
}
|
|
#endif
|
|
if (write(s, buf, len) < 0){
|
|
if (errno == EPIPE)
|
|
clixon_debug(CLIXON_DBG_DEFAULT, "write err SIGPIPE");
|
|
else
|
|
clixon_log(NULL, LOG_ERR, "%s: write: %s", __FUNCTION__, strerror(errno));
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Encapsulate and send outgoing netconf packet as cbuf on socket
|
|
*
|
|
* @param[in] framing Framing type, ie EOM(1.0) or chunked (1.1)
|
|
* @param[in] cb Cligen buffer that contains the XML message
|
|
* @retval 0 OK
|
|
* @retval -1 Error
|
|
* @note Assumes "cb" contains valid XML
|
|
* @see netconf_output without encapsulation
|
|
* @see netconf_hello_msg where framing is set
|
|
*/
|
|
int
|
|
netconf_output_encap(netconf_framing_type framing,
|
|
cbuf *cb)
|
|
{
|
|
int retval = -1;
|
|
|
|
if (netconf_framing_preamble(framing, cb) < 0)
|
|
goto done;
|
|
if (netconf_framing_postamble(framing, cb) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Track chunked framing defined in RFC6242
|
|
*
|
|
* The function works by calling sequentially each received char and using
|
|
* two state variables: state and size
|
|
* The function traverses the states 0-7, where state 4 also uses countdown of size
|
|
* The function returns one of four results: framing/control; chunk-data; end-of-frame; or error
|
|
* Anything not conforming is returned as error.
|
|
* Possibly one should accept whitespaces between frames?
|
|
* RFC6242 Sec 4.2
|
|
* <Chunked-Message> = <chunk>+ end-of-chunks
|
|
* <chunk> = LF HASH <chunk-size> LF <chunk-data>
|
|
* Example: \n#4\n
|
|
* data
|
|
* <end-of-chunks> = LF HASH HASH LF
|
|
* Example: \n##\n
|
|
* <chunk-size> = [1-9][0-9]*
|
|
*
|
|
* State: 0 No frame (begin/ended)
|
|
* 1 \n received
|
|
* 2 # received
|
|
* 3 read [1-9]
|
|
* 4 read chunk-size read: chunk-data
|
|
* @param[in] ch New input character
|
|
* @param[in,out] state State machine state
|
|
* @param[in,out] size Remaining expecting chunk bytes.
|
|
* @retval 2 End-of-frame
|
|
* @retval 1 Chunk-data
|
|
* @retval 0 Framing char, not data
|
|
* @retval -1 Framing error
|
|
* Example:
|
|
C: \n#4\n
|
|
C: <rpc
|
|
C: \n#18\n
|
|
C: message-id="102"\n
|
|
C: \n#79\n
|
|
C: xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">\n
|
|
C: <close-session/>\n
|
|
C: </rpc>
|
|
C: \n##\n
|
|
*/
|
|
int
|
|
netconf_input_chunked_framing(char ch,
|
|
int *state,
|
|
size_t *size)
|
|
{
|
|
int retval = 0;
|
|
|
|
clixon_debug(CLIXON_DBG_DEFAULT | CLIXON_DBG_DETAIL, "ch:%c(%d) state:%d size:%zu", ch, ch, *state, *size);
|
|
switch (*state){
|
|
case 0:
|
|
if (ch == '\n'){
|
|
(*state)++;
|
|
break;
|
|
}
|
|
clixon_err(OE_NETCONF, 0, "NETCONF framing error chunk-start: expected \\n but received %c (state:%d)", ch, *state);
|
|
goto err;
|
|
break;
|
|
case 1:
|
|
case 5:
|
|
if (ch == '#'){
|
|
(*state)++;
|
|
break;
|
|
}
|
|
clixon_err(OE_NETCONF, 0, "NETCONF framing error: expected # but received %c (state:%d)", ch, *state);
|
|
goto err;
|
|
break;
|
|
case 2:
|
|
if (ch == '#'){
|
|
(*state) = 0;
|
|
retval = 2; /* end of frame (empty data) */
|
|
break;
|
|
}
|
|
else if (ch >= '1' && ch <= '9'){ /* first num */
|
|
(*state)++;
|
|
*size = ch-'0';
|
|
break;
|
|
}
|
|
clixon_err(OE_NETCONF, 0, "NETCONF framing error chunk-start: expected 1-9 or # but received %c (state:%d)", ch, *state);
|
|
goto err;
|
|
break;
|
|
case 3:
|
|
if (ch >= '0' && ch <= '9'){ /* other nums */
|
|
*size = (*size)*10 + ch-'0';
|
|
break;
|
|
}
|
|
else if (ch == '\n'){
|
|
(*state)++;
|
|
break;
|
|
}
|
|
clixon_err(OE_NETCONF, 0, "NETCONF framing error chunk-size: expected 0-9 or \\n but received %c (state:%d)", ch, *state);
|
|
goto err;
|
|
break;
|
|
case 4:
|
|
if (*size > 0){ /* chunk-data */
|
|
(*size)--;
|
|
retval = 1; /* chunk-data */
|
|
break;
|
|
}
|
|
else if (*size == 0 && ch == '\n'){
|
|
(*state)++;
|
|
break;
|
|
}
|
|
clixon_err(OE_NETCONF, 0, "NETCONF framing error chunk-end: expected \\n but received %c (state:%d)", ch, *state);
|
|
goto err;
|
|
break;
|
|
case 6:
|
|
if (ch == '#'){
|
|
(*state)++;
|
|
break;
|
|
}
|
|
else if (ch >= '1' && ch <= '9'){ /* first num */
|
|
*state=3;
|
|
*size = ch-'0';
|
|
break;
|
|
}
|
|
clixon_err(OE_NETCONF, 0, "NETCONF framing error: expected # but received %c (state:%d)", ch, *state);
|
|
goto err;
|
|
break;
|
|
case 7:
|
|
if (ch == '\n'){
|
|
(*state) = 0;
|
|
retval = 2; /* end of frame */
|
|
break;
|
|
}
|
|
clixon_err(OE_NETCONF, 0, "NETCONF framing error chunk-end: expected \\n but received %c (state:%d)", ch, *state);
|
|
goto err;
|
|
break;
|
|
default:
|
|
clixon_err(OE_NETCONF, 0, "NETCONF framing error %c , invalid state:%d", ch, *state);
|
|
goto err;
|
|
break;
|
|
}
|
|
done:
|
|
return retval;
|
|
err:
|
|
*state = 0;
|
|
retval = -1; /* Error */
|
|
goto done;
|
|
}
|