* Restconf configuration has a new configure model: clixon-restconf.yang enabling restconf daemon configuration from datastore instead of from config file.

This commit is contained in:
Olof hagsand 2020-11-03 21:25:42 +01:00
parent 40da4421e6
commit 7a0838da3a
14 changed files with 1718 additions and 900 deletions

View file

@ -27,6 +27,23 @@
## 4.9.0 Expected: 15 Dec 2020 ## 4.9.0 Expected: 15 Dec 2020
### New features
* Restconf configuration has a new configure model: `clixon-restconf.yang` enabling restconf daemon configuration from datastore instead of from config file.
* Restconf config data, such as addresses, authentication type, etc, is read from the backend datastore instead of the clixon-config file on startup.
* This is enabled by setting `CLIXON_RESTCONF_CONFIG` to true (or start clixon-restconf with `-b`), in which case restconf data can be set in the datastore.
* This only applies to the evhtp restconf daemon, not fcgi/nginx.
* If enabled, most RESTCONF clixon-config options are obsolete
* Thanks to Dave Cornejo for the idea
* Example: instead of setting `<CLICON_SSL_SERVER_CERT>file</CLICON_SSL_SERVER_CERT>` in clixon.xml, set: `arestconf><socket><server-cert-path>file</server-cert-path></socket></restconf>` in the running datastore before starting restconf.
### API changes on existing protocol/config features
Users may have to change how they access the system
* New clixon-config@2020-11-03.yang revision
* Added option: `CLICON_RESTCONF_CONFIG` for reading restconf daemon config frm datastore
### Minor changes ### Minor changes
* Added new backend plugin: ca_pre-demon called if backend is daemonized just prior to forking. * Added new backend plugin: ca_pre-demon called if backend is daemonized just prior to forking.

View file

@ -79,26 +79,35 @@
#include "restconf_err.h" #include "restconf_err.h"
#include "restconf_root.h" #include "restconf_root.h"
/* Command line options to be passed to getopt(3) */ /* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:ro:scP:" #define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:ro:bscP:"
/* See see listen(5) */ /* See see listen(5) */
#define SOCKET_LISTEN_BACKLOG 16 #define SOCKET_LISTEN_BACKLOG 16
/* Need global variable to for signal handler XXX */ /* clixon evhtp handle */
static clicon_handle _CLICON_HANDLE = NULL; typedef struct {
static struct evhtp_handle{
evhtp_t *eh_htp; evhtp_t *eh_htp;
struct event_base *eh_evbase; struct event_base *eh_evbase;
evhtp_ssl_cfg_t *eh_ssl_config; evhtp_ssl_cfg_t *eh_ssl_config;
} _EVHTP_HANDLE = {0,}; } cx_evhtp_handle;
/* Need this global to pass to signal handler
* XXX Try to get rid of code in signal handler
*/
static cx_evhtp_handle *_EVHTP_HANDLE = NULL;
/* Need global variable to for signal handler XXX */
static clicon_handle _CLICON_HANDLE = NULL;
static void static void
evhtp_terminate(struct evhtp_handle *eh) evhtp_terminate(cx_evhtp_handle *eh)
{ {
evhtp_ssl_cfg_t *sc; evhtp_ssl_cfg_t *sc;
if (eh == NULL)
return;
if (eh->eh_htp){ if (eh->eh_htp){
evhtp_unbind_socket(eh->eh_htp); evhtp_unbind_socket(eh->eh_htp);
evhtp_free(eh->eh_htp); evhtp_free(eh->eh_htp);
@ -114,9 +123,11 @@ evhtp_terminate(struct evhtp_handle *eh)
free(sc->privfile); free(sc->privfile);
free(sc); free(sc);
} }
free(eh);
} }
/*! Signall terminates process /*! Signall terminates process
* XXX Try to get rid of code in signal handler -> so we can get rid of global variables
*/ */
static void static void
restconf_sig_term(int arg) restconf_sig_term(int arg)
@ -128,7 +139,8 @@ restconf_sig_term(int arg)
__PROGRAM__, __FUNCTION__, getpid(), arg); __PROGRAM__, __FUNCTION__, getpid(), arg);
else else
exit(-1); exit(-1);
evhtp_terminate(&_EVHTP_HANDLE); if (_EVHTP_HANDLE) /* global */
evhtp_terminate(_EVHTP_HANDLE);
if (_CLICON_HANDLE){ if (_CLICON_HANDLE){
// stream_child_freeall(_CLICON_HANDLE); // stream_child_freeall(_CLICON_HANDLE);
restconf_terminate(_CLICON_HANDLE); restconf_terminate(_CLICON_HANDLE);
@ -492,8 +504,83 @@ cx_path_restconf(evhtp_request_t *req,
return; /* void */ return; /* void */
} }
/*! Get Server cert ssl info
* @param[in] h Clicon handle
* @param[in] server_cert_path Path to server ssl cert file
* @param[in] server_key_path Path to server ssl key file
* @param[in,out] ssl_config evhtp ssl config struct
* @retval 0 OK
* @retval -1 Error
*/
static int
cx_get_ssl_server_certs(clicon_handle h,
const char *server_cert_path,
const char *server_key_path,
evhtp_ssl_cfg_t *ssl_config)
{
int retval = -1;
struct stat f_stat;
if (ssl_config == NULL || server_cert_path == NULL){
clicon_err(OE_CFG, EINVAL, "Input parameter is NULL");
goto done;
}
if ((ssl_config->pemfile = strdup(server_cert_path)) == NULL){
clicon_err(OE_CFG, errno, "strdup");
goto done;
}
if (stat(ssl_config->pemfile, &f_stat) != 0) {
clicon_err(OE_FATAL, errno, "Cannot load SSL cert '%s'", ssl_config->pemfile);
goto done;
}
if ((ssl_config->privfile = strdup(server_key_path)) == NULL){
clicon_err(OE_CFG, errno, "strdup");
goto done;
}
if (stat(ssl_config->privfile, &f_stat) != 0) {
clicon_err(OE_FATAL, errno, "Cannot load SSL key '%s'", ssl_config->privfile);
goto done;
}
retval = 0;
done:
return retval;
}
/*! Get client ssl cert info
* @param[in] h Clicon handle
* @param[in] server_ca_cert_path Path to server ssl CA file for client certs
* @param[in,out] ssl_config evhtp ssl config struct
* @retval 0 OK
* @retval -1 Error
*/
static int
cx_get_ssl_client_certs(clicon_handle h,
const char *server_ca_cert_path,
evhtp_ssl_cfg_t *ssl_config)
{
int retval = -1;
struct stat f_stat;
if (ssl_config == NULL || server_ca_cert_path == NULL){
clicon_err(OE_CFG, EINVAL, "Input parameter is NULL");
goto done;
}
if ((ssl_config->cafile = strdup(server_ca_cert_path)) == NULL){
clicon_err(OE_CFG, errno, "strdup");
goto done;
}
if (stat(ssl_config->cafile, &f_stat) != 0) {
clicon_err(OE_FATAL, errno, "Cannot load SSL key '%s'", ssl_config->privfile);
goto done;
}
retval = 0;
done:
return retval;
}
/*! Get Server cert info /*! Get Server cert info
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] ssl_verify_clients If true, verify client certs
* @param[out] ssl_config evhtp ssl config struct * @param[out] ssl_config evhtp ssl config struct
*/ */
static int static int
@ -602,10 +689,11 @@ usage(clicon_handle h,
"\t-a UNIX|IPv4|IPv6 Internal backend socket family\n" "\t-a UNIX|IPv4|IPv6 Internal backend socket family\n"
"\t-u <path|addr>\t Internal socket domain path or IP addr (see -a)\n" "\t-u <path|addr>\t Internal socket domain path or IP addr (see -a)\n"
"\t-r \t\t Do not drop privileges if run as root\n" "\t-r \t\t Do not drop privileges if run as root\n"
"\t-b \t\t Read config from backend - not local (same as CLICON_RESTCONF_CONF=true) \n"
"\t-o <option>=<value> Set configuration option overriding config file (see clixon-config.yang)\n" "\t-o <option>=<value> Set configuration option overriding config file (see clixon-config.yang)\n"
"\t-s\t\t SSL server, https\n" "\t-s\t\t SSL server, https (local config)\n"
"\t-c\t\t SSL verify client certs\n" "\t-c\t\t SSL verify client certs (local config)\n"
"\t-P <port>\t HTTP port (default 80, or 443 if -s is given)\n" "\t-P <port>\t HTTP port (default 80, or 443 if -s is given) (local config)\n"
, ,
argv0, argv0,
clicon_restconf_dir(h) clicon_restconf_dir(h)
@ -613,37 +701,255 @@ usage(clicon_handle h,
exit(0); exit(0);
} }
/*! Main routine for libevhtp restconf /*! Main routine for libevhtp restconf
*/ */
/*! Phase 2 of start per-socket config
* @param[in] h Clicon handle
* @param[in] xs socket config
* @param[in] nsc Namespace context
* @param[out] namespace
* @param[out] address
* @param[out] port
* @param[out] ssl
*/
static int
cx_evhtp_socket(clicon_handle h,
cxobj *xs,
cvec *nsc,
char **namespace,
char **address,
uint16_t *port,
uint16_t *ssl)
{
int retval = -1;
cxobj *x;
char *str = NULL;
char *reason = NULL;
int ret;
if ((x = xpath_first(xs, nsc, "namespace")) == NULL){
clicon_err(OE_XML, EINVAL, "Mandatory namespace not given");
goto done;
}
*namespace = xml_body(x);
if ((x = xpath_first(xs, nsc, "address")) == NULL){
clicon_err(OE_XML, EINVAL, "Mandatory address not given");
goto done;
}
*address = xml_body(x);
if ((x = xpath_first(xs, nsc, "port")) != NULL &&
(str = xml_body(x)) != NULL){
if ((ret = parse_uint16(str, port, &reason)) < 0){
clicon_err(OE_XML, errno, "parse_uint16");
goto done;
}
if (ret == 0){
clicon_err(OE_XML, EINVAL, "Unrecognized value of port: %s", str);
goto done;
}
}
if ((x = xpath_first(xs, nsc, "ssl")) != NULL &&
(str = xml_body(x)) != NULL){
/* XXX use parse_bool but it is legacy static */
if (strcmp(str, "false") == 0)
*ssl = 0;
else if (strcmp(str, "true") == 0)
*ssl = 1;
else {
clicon_err(OE_XML, EINVAL, "Unrecognized value of ssl: %s", str);
goto done;
}
}
retval = 0;
done:
if (reason)
free(reason);
return retval;
}
/*! Phase 2 of evhtp init, config has been retrieved from backend
* @param[in] h Clicon handle
* @param[in] xconfig XML config
* @param[in] nsc Namespace context
* @param[in] eh Evhtp handle
* @note only one socket allowed in this implementation
*/
static int
cx_evhtp_init(clicon_handle h,
cxobj *xconfig,
cvec *nsc,
cx_evhtp_handle *eh)
{
int retval = -1;
int auth_type_client_certifificate = 0;
uint16_t port = 0;
cxobj *xrestconf;
cxobj **vec = NULL;
size_t veclen;
char *auth_type = NULL;
char *server_cert_path = NULL;
char *server_key_path = NULL;
char *server_ca_cert_path = NULL;
//XXX char *client_cert_ca = NULL;
cxobj *x;
char *namespace = NULL;
char *address = NULL;
uint16_t use_ssl_server = 0;
/* Extract socket fields from xconfig */
if ((xrestconf = xpath_first(xconfig, nsc, "restconf")) == NULL){
clicon_err(OE_CFG, ENOENT, "restconf top symbol not found");
goto done;
}
/* get common fields */
if ((x = xpath_first(xrestconf, nsc, "auth-type")) != NULL) /* XXX: leaf-list? */
auth_type = xml_body(x);
if (auth_type && strcmp(auth_type, "client-certificate") == 0)
auth_type_client_certifificate = 1;
if ((x = xpath_first(xrestconf, nsc, "server-cert-path")) != NULL)
server_cert_path = xml_body(x);
if ((x = xpath_first(xrestconf, nsc, "server-key-path")) != NULL)
server_key_path = xml_body(x);
if ((x = xpath_first(xrestconf, nsc, "server-ca-cert-path")) != NULL)
server_ca_cert_path = xml_body(x);
// XXX if ((x = xpath_first(xrestconf, nsc, "client-cert-ca")) != NULL)
// XXX client_cert_ca = xml_body(x);
/* get the list of socket config-data */
if (xpath_vec(xrestconf, nsc, "socket", &vec, &veclen) < 0)
goto done;
/* Accept only a single socket XXX */
if (veclen != 1){
clicon_err(OE_XML, EINVAL, "Only single socket supported"); /* XXX warning: accept more? */
goto done;
}
if (cx_evhtp_socket(h, vec[0], nsc, &namespace, &address, &port, &use_ssl_server) < 0)
goto done;
if (use_ssl_server &&
(server_cert_path==NULL || server_key_path == NULL)){
clicon_err(OE_XML, EINVAL, "Enabled SSL server requires server_cert_path and server_key_path");
goto done;
}
if (auth_type_client_certifificate){
if (!use_ssl_server){
clicon_err(OE_XML, EINVAL, "Client certificate authentication type requires SSL");
goto done;
}
if (server_ca_cert_path == NULL){
clicon_err(OE_XML, EINVAL, "Client certificate authentication type requires server-ca-cert-path");
goto done;
}
}
/* Init evhtp */
if ((eh->eh_evbase = event_base_new()) == NULL){
clicon_err(OE_UNIX, errno, "event_base_new");
goto done;
}
/* create a new evhtp_t instance */
if ((eh->eh_htp = evhtp_new(eh->eh_evbase, NULL)) == NULL){
clicon_err(OE_UNIX, errno, "evhtp_new");
goto done;
}
if (use_ssl_server){
/* Init evhtp ssl config struct */
if ((eh->eh_ssl_config = malloc(sizeof(evhtp_ssl_cfg_t))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(eh->eh_ssl_config, 0, sizeof(evhtp_ssl_cfg_t));
eh->eh_ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
if (cx_get_ssl_server_certs(h, server_cert_path,
server_key_path,
eh->eh_ssl_config) < 0)
goto done;
if (auth_type_client_certifificate)
if (cx_get_ssl_client_certs(h, server_ca_cert_path, eh->eh_ssl_config) < 0)
goto done;
eh->eh_ssl_config->x509_verify_cb = cx_verify_certs; /* Is extra verification necessary? */
if (auth_type_client_certifificate){
eh->eh_ssl_config->verify_peer = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
eh->eh_ssl_config->x509_verify_cb = cx_verify_certs;
eh->eh_ssl_config->verify_depth = 2;
}
// ssl_verify_mode = htp_sslutil_verify2opts(optarg);
}
assert(SSL_VERIFY_NONE == 0);
/* Init evhtp */
if ((eh->eh_evbase = event_base_new()) == NULL){
clicon_err(OE_UNIX, errno, "event_base_new");
goto done;
}
/* create a new evhtp_t instance */
if ((eh->eh_htp = evhtp_new(eh->eh_evbase, NULL)) == NULL){
clicon_err(OE_UNIX, errno, "evhtp_new");
goto done;
}
/* Here the daemon either uses SSL or not, ie you cant seem to mix http and https :-( */
if (use_ssl_server){
if (evhtp_ssl_init(eh->eh_htp, eh->eh_ssl_config) < 0){
clicon_err(OE_UNIX, errno, "evhtp_new");
goto done;
}
}
#ifndef EVHTP_DISABLE_EVTHR /* threads */
evhtp_use_threads_wexit(eh->eh_htp, NULL, NULL, 4, NULL);
#endif
/* Callback before the connection is accepted. */
evhtp_set_pre_accept_cb(eh->eh_htp, cx_pre_accept, h);
/* Callback right after a connection is accepted. */
evhtp_set_post_accept_cb(eh->eh_htp, cx_post_accept, h);
/* Callback to be executed for all /restconf api calls */
if (evhtp_set_cb(eh->eh_htp, "/" RESTCONF_API, cx_path_restconf, h) == NULL){
clicon_err(OE_EVENTS, errno, "evhtp_set_cb");
goto done;
}
/* Callback to be executed for all /restconf api calls */
if (evhtp_set_cb(eh->eh_htp, RESTCONF_WELL_KNOWN, cx_path_wellknown, h) == NULL){
clicon_err(OE_EVENTS, errno, "evhtp_set_cb");
goto done;
}
/* Generic callback called if no other callbacks are matched */
evhtp_set_gencb(eh->eh_htp, cx_gencb, h);
if (evhtp_bind_socket(eh->eh_htp, /* evhtp handle */
address, /* string address, eg ipv4:<ipv4addr> */
port, /* port */
SOCKET_LISTEN_BACKLOG /* backlog flag, see listen(5) */
) < 0){
clicon_err(OE_UNIX, errno, "evhtp_bind_socket");
goto done;
}
retval = 0;
done:
if (vec)
free(vec);
return retval;
}
/*! Read config from backend */
int int
main(int argc, restconf_config_backend(clicon_handle h,
char **argv) int argc,
char **argv,
int drop_privileges)
{ {
int retval = -1; int retval = -1;
char *argv0 = argv[0]; char *argv0 = argv[0];
int c;
clicon_handle h;
char *dir; char *dir;
int logdst = CLICON_LOG_SYSLOG;
yang_stmt *yspec = NULL; yang_stmt *yspec = NULL;
char *str; char *str;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
cvec *nsctx_global = NULL; /* Global namespace context */ cvec *nsctx_global = NULL; /* Global namespace context */
size_t cligen_buflen; size_t cligen_buflen;
size_t cligen_bufthreshold; size_t cligen_bufthreshold;
uint16_t defaultport; cvec *nsc = NULL;
uint16_t port = 0; cxobj *xconfig = NULL;
int dbg = 0; uint32_t id = 0; /* Session id, to poll backend up */
int use_ssl = 0; cx_evhtp_handle *eh = NULL;
int ssl_verify_clients = 0;
char *restconf_ipv4_addr = NULL;
char *restconf_ipv6_addr = NULL;
int i;
struct evhtp_handle *eh = &_EVHTP_HANDLE;
int drop_priveleges = 1;
#if 0
/* In the startup, logs to stderr & debug flag set later */ /* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst); clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@ -705,13 +1011,6 @@ main(int argc,
/* Find and read configfile */ /* Find and read configfile */
if (clicon_options_main(h) < 0) if (clicon_options_main(h) < 0)
goto done; goto done;
// stream_path = clicon_option_str(h, "CLICON_STREAM_PATH");
/* Start with http default port, but change this later if -s is set to https default port */
if ((i = clicon_option_int(h, "CLICON_RESTCONF_HTTP_PORT")) < 0){
clicon_err(OE_CFG, EINVAL, "CLICON_RESTCONF_HTTP_PORT not found");
goto done;
}
defaultport = (uint16_t)i;
/* Now rest of options, some overwrite option file */ /* Now rest of options, some overwrite option file */
optind = 1; optind = 1;
@ -745,7 +1044,7 @@ main(int argc,
clicon_option_str_set(h, "CLICON_SOCK", optarg); clicon_option_str_set(h, "CLICON_SOCK", optarg);
break; break;
case 'r':{ /* Do not drop privileges if run as root */ case 'r':{ /* Do not drop privileges if run as root */
drop_priveleges = 0; drop_privileges = 0;
break; break;
} }
case 'o':{ /* Configuration option */ case 'o':{ /* Configuration option */
@ -757,32 +1056,178 @@ main(int argc,
goto done; goto done;
break; break;
} }
case 's': /* ssl: use https */
use_ssl = 1;
/* Set to port - note can be overrifden by -P */
if ((i = clicon_option_int(h, "CLICON_RESTCONF_HTTPS_PORT")) < 0){
clicon_err(OE_CFG, EINVAL, "CLICON_RESTCONF_HTTPS_PORT not found");
goto done;
}
defaultport = (uint16_t)i;
break;
case 'c': /* ssl: verify clients */
ssl_verify_clients = 1;
break;
case 'P': /* http port */
if (!strlen(optarg))
usage(h, argv0);
port=atoi(optarg);
break;
default: default:
usage(h, argv0); usage(h, argv0);
break; break;
} }
argc -= optind; argc -= optind;
argv += optind; argv += optind;
#endif
/* Set default namespace according to CLICON_NAMESPACE_NETCONF_DEFAULT */
xml_nsctx_namespace_netconf_default(h);
assert(SSL_VERIFY_NONE == 0);
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);
#if 0 /* Drop privileges after evhtp and server key/cert read */
if (drop_privileges){
/* Drop privileges to WWWUSER if started as root */
if (restconf_drop_privileges(h, WWWUSER) < 0)
goto done;
}
#endif
/* Init cligen buffers */
cligen_buflen = clicon_option_int(h, "CLICON_CLI_BUF_START");
cligen_bufthreshold = clicon_option_int(h, "CLICON_CLI_BUF_THRESHOLD");
cbuf_alloc_set(cligen_buflen, cligen_bufthreshold);
/* Add (hardcoded) netconf features in case ietf-netconf loaded here
* Otherwise it is loaded in netconf_module_load below
*/
if (netconf_module_features(h) < 0)
goto done;
/* Create top-level yang spec and store as option */
if ((yspec = yspec_new()) == NULL)
goto done;
clicon_dbspec_yang_set(h, yspec);
/* Treat unknown XML as anydata */
if (clicon_option_bool(h, "CLICON_YANG_UNKNOWN_ANYDATA") == 1)
xml_bind_yang_unknown_anydata(1);
/* Load restconf plugins before yangs are loaded (eg extension callbacks) */
if ((dir = clicon_restconf_dir(h)) != NULL)
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0)
return -1;
/* Create a pseudo-plugin to create extension callback to set the ietf-routing
* yang-data extension for api-root top-level restconf function.
*/
if (clixon_pseudo_plugin(h, "pseudo restconf", &cp) < 0)
goto done;
cp->cp_api.ca_extension = restconf_main_extension_cb;
/* Load Yang modules
* 1. Load a yang module as a specific absolute filename */
if ((str = clicon_yang_main_file(h)) != NULL){
if (yang_spec_parse_file(h, str, yspec) < 0)
goto done;
}
/* 2. Load a (single) main module */
if ((str = clicon_yang_module_main(h)) != NULL){
if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
yspec) < 0)
goto done;
}
/* 3. Load all modules in a directory */
if ((str = clicon_yang_main_dir(h)) != NULL){
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
}
/* Load clixon lib yang module */
if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
goto done;
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
/* Load yang restconf module */
if (yang_spec_parse_module(h, "ietf-restconf", NULL, yspec)< 0)
goto done;
/* Add netconf yang spec, used as internal protocol */
if (netconf_module_load(h) < 0)
goto done;
/* Add system modules */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
goto done;
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
/* Here all modules are loaded
* Compute and set canonical namespace context
*/
if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0)
goto done;
if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done;
/* Query backend of config.
* Before evhtp, try again if not done */
while (1){
if (clicon_session_id_get(h, &id) < 0){
if (errno == ENOENT){
fprintf(stderr, "waiting");
sleep(1);
continue;
}
}
clicon_session_id_set(h, id);
break;
}
if ((nsc = xml_nsctx_init(NULL, "https://clicon.org/restconf")) == NULL)
goto done;
if (clicon_rpc_get_config(h, NULL, "running", "/restconf", nsc, &xconfig) < 0)
goto done;
/* Initialize evhtp handle - fill it in cx_evhtp_init */
if ((eh = malloc(sizeof *eh)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(eh, 0, sizeof *eh);
_EVHTP_HANDLE = eh; /* global */
if (cx_evhtp_init(h, xconfig, nsc, eh) < 0)
goto done;
/* Drop privileges after evhtp and server key/cert read */
if (drop_privileges){
/* Drop privileges to WWWUSER if started as root */
if (restconf_drop_privileges(h, WWWUSER) < 0)
goto done;
}
/* libevent main loop */
event_base_loop(eh->eh_evbase, 0); /* XXX: replace with clixon_event_loop() */
retval = 0;
done:
if (xconfig)
xml_free(xconfig);
if (nsc)
cvec_free(nsc);
clicon_debug(1, "restconf_main_evhtp done");
// stream_child_freeall(h);
evhtp_terminate(eh);
restconf_terminate(h);
return retval;
}
/*! Read config locally */
int
restconf_config_local(clicon_handle h,
int argc,
char **argv,
uint16_t port,
int ssl_verify_clients,
int use_ssl,
int drop_privileges)
{
int retval = -1;
char *argv0 = argv[0];
char *dir;
yang_stmt *yspec = NULL;
char *str;
clixon_plugin *cp = NULL;
cvec *nsctx_global = NULL; /* Global namespace context */
size_t cligen_buflen;
size_t cligen_bufthreshold;
char *restconf_ipv4_addr = NULL;
char *restconf_ipv6_addr = NULL;
cx_evhtp_handle *eh = NULL;
/* port = defaultport unless explicitly set -P */ /* port = defaultport unless explicitly set -P */
if (port == 0)
port = defaultport;
if (port == 0){ if (port == 0){
clicon_err(OE_DAEMON, EINVAL, "Restconf bind port is 0"); clicon_err(OE_DAEMON, EINVAL, "Restconf bind port is 0");
goto done; goto done;
@ -790,6 +1235,13 @@ main(int argc,
/* Set default namespace according to CLICON_NAMESPACE_NETCONF_DEFAULT */ /* Set default namespace according to CLICON_NAMESPACE_NETCONF_DEFAULT */
xml_nsctx_namespace_netconf_default(h); xml_nsctx_namespace_netconf_default(h);
/* Initialize evhtp handle */
if ((eh = malloc(sizeof *eh)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(eh, 0, sizeof *eh);
_EVHTP_HANDLE = eh; /* global */
/* Check server ssl certs */ /* Check server ssl certs */
if (use_ssl){ if (use_ssl){
/* Init evhtp ssl config struct */ /* Init evhtp ssl config struct */
@ -835,7 +1287,6 @@ main(int argc,
#ifndef EVHTP_DISABLE_EVTHR #ifndef EVHTP_DISABLE_EVTHR
evhtp_use_threads_wexit(eh->eh_htp, NULL, NULL, 4, NULL); evhtp_use_threads_wexit(eh->eh_htp, NULL, NULL, 4, NULL);
#endif #endif
/* Callback before the connection is accepted. */ /* Callback before the connection is accepted. */
evhtp_set_pre_accept_cb(eh->eh_htp, cx_pre_accept, h); evhtp_set_pre_accept_cb(eh->eh_htp, cx_pre_accept, h);
@ -901,7 +1352,8 @@ main(int argc,
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
} }
if (drop_priveleges){
if (drop_privileges){
/* Drop privileges to WWWUSER if started as root */ /* Drop privileges to WWWUSER if started as root */
if (restconf_drop_privileges(h, WWWUSER) < 0) if (restconf_drop_privileges(h, WWWUSER) < 0)
goto done; goto done;
@ -984,9 +1436,10 @@ main(int argc,
if (clicon_nsctx_global_set(h, nsctx_global) < 0) if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done; goto done;
/* Dump configuration options on debug */ /* Call start function in all plugins before we go interactive
if (dbg) */
clicon_option_dump(h, dbg); if (clixon_plugin_start_all(h) < 0)
goto done;
/* Call start function in all plugins before we go interactive /* Call start function in all plugins before we go interactive
*/ */
@ -999,7 +1452,196 @@ main(int argc,
done: done:
clicon_debug(1, "restconf_main_evhtp done"); clicon_debug(1, "restconf_main_evhtp done");
// stream_child_freeall(h); // stream_child_freeall(h);
evhtp_terminate(&_EVHTP_HANDLE); evhtp_terminate(eh);
restconf_terminate(h);
return retval;
}
int
main(int argc,
char **argv)
{
int retval = -1;
char *argv0 = argv[0];
int c;
clicon_handle h;
int logdst = CLICON_LOG_SYSLOG;
int dbg = 0;
int i;
cx_evhtp_handle *eh = NULL;
int drop_privileges = 1;
uint16_t defaultport = 0;
int use_ssl = 0;
int ssl_verify_clients = 0;
uint16_t port = 0;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
/* Create handle */
if ((h = restconf_handle_init()) == NULL)
goto done;
_CLICON_HANDLE = h; /* for termination handling */
while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1)
switch (c) {
case 'h':
usage(h, argv0);
break;
case 'D' : /* debug */
if (sscanf(optarg, "%d", &dbg) != 1)
usage(h, argv0);
break;
case 'f': /* override config file */
if (!strlen(optarg))
usage(h, argv0);
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
break;
case 'E': /* extra config directory */
if (!strlen(optarg))
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg);
break;
case 'l': /* Log destination: s|e|o */
if ((logdst = clicon_log_opt(optarg[0])) < 0)
usage(h, argv0);
if (logdst == CLICON_LOG_FILE &&
strlen(optarg)>1 &&
clicon_log_file(optarg+1) < 0)
goto done;
break;
} /* switch getopt */
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
clicon_debug_init(dbg, NULL);
clicon_log(LOG_NOTICE, "%s: %u Started", __PROGRAM__, getpid());
if (set_signal(SIGTERM, restconf_sig_term, NULL) < 0){
clicon_err(OE_DAEMON, errno, "Setting signal");
goto done;
}
if (set_signal(SIGINT, restconf_sig_term, NULL) < 0){
clicon_err(OE_DAEMON, errno, "Setting signal");
goto done;
}
if (set_signal(SIGCHLD, restconf_sig_child, NULL) < 0){
clicon_err(OE_DAEMON, errno, "Setting signal");
goto done;
}
/* Find and read configfile */
if (clicon_options_main(h) < 0)
goto done;
// stream_path = clicon_option_str(h, "CLICON_STREAM_PATH");
/* XXX only local conf */
defaultport = (uint16_t)clicon_option_int(h, "CLICON_RESTCONF_HTTP_PORT");
/* Now rest of options, some overwrite option file */
optind = 1;
opterr = 0;
while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1)
switch (c) {
case 'h' : /* help */
case 'D' : /* debug */
case 'f': /* config file */
case 'E': /* extra config dir */
case 'l': /* log */
break; /* see above */
case 'p' : /* yang dir path */
if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
goto done;
break;
case 'd': /* Plugin directory */
if (!strlen(optarg))
usage(h, argv0);
clicon_option_str_set(h, "CLICON_RESTCONF_DIR", optarg);
break;
case 'y' : /* Load yang spec file (override yang main module) */
clicon_option_str_set(h, "CLICON_YANG_MAIN_FILE", optarg);
break;
case 'a': /* internal backend socket address family */
clicon_option_str_set(h, "CLICON_SOCK_FAMILY", optarg);
break;
case 'u': /* internal backend socket unix domain path or ip host */
if (!strlen(optarg))
usage(h, argv0);
clicon_option_str_set(h, "CLICON_SOCK", optarg);
break;
case 'r':{ /* Do not drop privileges if run as root */
drop_privileges = 0;
break;
}
case 'o':{ /* Configuration option */
char *val;
if ((val = index(optarg, '=')) == NULL)
usage(h, argv0);
*val++ = '\0';
if (clicon_option_add(h, optarg, val) < 0)
goto done;
break;
}
case 'b': /* Read config from backend - not local */
clicon_option_bool_set(h, "CLICON_RESTCONF_CONFIG", 1);
break;
case 's': /* ssl: use https */
use_ssl = 1;
/* Set to port - note can be overrifden by -P */
if ((i = clicon_option_int(h, "CLICON_RESTCONF_HTTPS_PORT")) < 0){
clicon_err(OE_CFG, EINVAL, "CLICON_RESTCONF_HTTPS_PORT not found");
goto done;
}
defaultport = (uint16_t)i;
break;
case 'c': /* ssl: verify clients */
ssl_verify_clients = 1;
break;
case 'P': /* http port */
if (!strlen(optarg))
usage(h, argv0);
port=atoi(optarg);
break;
default:
usage(h, argv0);
break;
}
argc -= optind;
argv += optind;
/* Dump configuration options on debug */
if (dbg)
clicon_option_dump(h, dbg);
/* port = defaultport unless explicitly set -P */
if (port == 0)
port = defaultport;
if (clicon_option_bool(h, "CLICON_RESTCONF_CONFIG") == 0){
/* Read config locally */
if (restconf_config_local(h, argc, argv,
port,
ssl_verify_clients,
use_ssl,
drop_privileges
) < 0)
goto done;
}
else {
/* Read config from backend */
if (restconf_config_backend(h, argc, argv, drop_privileges) < 0)
goto done;
}
event_base_loop(eh->eh_evbase, 0);
retval = 0;
done:
clicon_debug(1, "restconf_main_evhtp done");
// stream_child_freeall(h);
evhtp_terminate(eh);
restconf_terminate(h); restconf_terminate(h);
return retval; return retval;
} }

View file

@ -1379,6 +1379,10 @@ netconf_module_load(clicon_handle h)
if (clicon_option_bool(h, "CLICON_XML_CHANGELOG")) if (clicon_option_bool(h, "CLICON_XML_CHANGELOG"))
if (yang_spec_parse_module(h, "clixon-xml-changelog", NULL, yspec)< 0) if (yang_spec_parse_module(h, "clixon-xml-changelog", NULL, yspec)< 0)
goto done; goto done;
/* Clixon restconf daemon */
if (clicon_option_bool(h, "CLICON_RESTCONF_CONFIG"))
if (yang_spec_parse_module(h, "clixon-restconf", NULL, yspec)< 0)
goto done;
retval = 0; retval = 0;
done: done:
return retval; return retval;

View file

@ -221,7 +221,7 @@ fi
fi # false fi # false
new "netconf module ietf-inet-types" new "netconf module ietf-inet-types"
expect="<module><name>ietf-inet-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace><conformance-type>implement</conformance-type></module>" expect="<module><name>ietf-inet-types</name><revision>2020-07-06</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace><conformance-type>implement</conformance-type></module>"
match=`echo "$ret" | grep --null -Go "$expect"` match=`echo "$ret" | grep --null -Go "$expect"`
if [ -z "$match" ]; then if [ -z "$match" ]; then
err "$expect" "$ret" err "$expect" "$ret"

View file

@ -0,0 +1,120 @@
#!/usr/bin/env bash
# New Restconf config using backend config
# DOES NOT WORK WITH FCGI
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
# Skip it other than evhtp
if [ "${WITH_RESTCONF}" != "evhtp" ]; then
if [ "$s" = $0 ]; then exit 0; else return 0; fi # skip
fi
APPNAME=example
cfg=$dir/conf.xml
# Use yang in example
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
<CLICON_YANG_MODULE_MAIN>clixon-example</CLICON_YANG_MODULE_MAIN>
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_REGEXP>example_backend.so$</CLICON_BACKEND_REGEXP>
<CLICON_RESTCONF_CONFIG>true</CLICON_RESTCONF_CONFIG>
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895>
</clixon-config>
EOF
if [ ${RCPROTO} = "https" ]; then
ssl=true
port=443
else
ssl=false
port=80
fi
cat<<EOF > $dir/startup_db
<config>
<restconf xmlns="https://clicon.org/restconf">
<socket>
<namespace>default</namespace>
<address>0.0.0.0</address>
<port>$port</port>
<ssl>$ssl</ssl>
</socket>
<auth-type>password</auth-type>
<server-cert-path>/etc/ssl/certs/clixon-server-crt.pem</server-cert-path>
<server-key-path>/etc/ssl/private/clixon-server-key.pem</server-key-path>
<server-ca-cert-path>/etc/ssl/certs/clixon-ca_crt.pem</server-ca-cert-path>
<client-cert-ca></client-cert-ca>
</restconf>
</config>
EOF
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg -s startup
if [ $? -ne 0 ]; then
err
fi
sudo pkill -f clixon_backend # to be sure
new "start backend -s startup -f $cfg"
start_backend -s startup -f $cfg
fi
new "wait backend"
wait_backend
if [ $RC -ne 0 ]; then
new "kill old restconf daemon"
stop_restconf_pre
new "start restconf daemon"
start_restconf -f $cfg
fi
new "wait restconf"
wait_restconf
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
new "restconf get restconf resource. RFC 8040 3.3 (json)"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf)" 0 'HTTP/1.1 200 OK' '{"ietf-restconf:restconf":{"data":{},"operations":{},"yang-library-version":"2016-06-21"}}'
new "restconf get restconf resource. RFC 8040 3.3 (xml)"
# Get XML instead of JSON?
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf)" 0 'HTTP/1.1 200 OK' '<restconf xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf>'
if [ $RC -ne 0 ]; then
new "Kill restconf daemon"
stop_restconf
fi
if [ $BE -eq 0 ]; then
exit # BE
fi
new "Kill backend"
# Check if premature kill
pid=$(pgrep -u root -f clixon_backend)
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
stop_backend -f $cfg
rm -rf $dir

View file

@ -101,7 +101,7 @@ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPR
# This just catches the header and the jukebox module, the RFC has foo and bar which # This just catches the header and the jukebox module, the RFC has foo and bar which
# seems wrong to recreate # seems wrong to recreate
new "B.1.2. Retrieve the Server Module Information" new "B.1.2. Retrieve the Server Module Information"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":"0","module":\[{"name":"clixon-lib","revision":"2020-04-23","namespace":"http://clicon.org/lib","conformance-type":"implement"},{"name":"example-events","revision":"","namespace":"urn:example:events","conformance-type":"implement"},{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"},{"name":"example-system","revision":"","namespace":"http://example.com/ns/example-system","conformance-type":"implement"},{"name":"ietf-inet-types","revision":"2013-07-15","namespace":"urn:ietf:params:xml:ns:yang:ietf-inet-types","conformance-type":"implement"},' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":"0","module":\[{"name":"clixon-lib","revision":"2020-04-23","namespace":"http://clicon.org/lib","conformance-type":"implement"},{"name":"example-events","revision":"","namespace":"urn:example:events","conformance-type":"implement"},{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"},{"name":"example-system","revision":"","namespace":"http://example.com/ns/example-system","conformance-type":"implement"}'
new "B.1.3. Retrieve the Server Capability Information" new "B.1.3. Retrieve the Server Capability Information"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability> expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability>

View file

@ -301,7 +301,7 @@ cat <<EOF > $dir/startup_db
</config> </config>
EOF EOF
MODSTATE='<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"><module-set-id>0</module-set-id><module><name>clixon-lib</name><revision>2020-04-23</revision><namespace>http://clicon.org/lib</namespace></module><module><name>ietf-inet-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace></module><module><name>ietf-netconf</name><revision>2011-06-01</revision><namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace></module><module><name>ietf-restconf</name><revision>2017-01-26</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-restconf</namespace></module><module><name>ietf-yang-library</name><revision>2016-06-21</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace></module><module><name>ietf-yang-types</name><revision>2013-07-15</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace></module><module><name>interfaces</name><revision>2018-02-20</revision><namespace>urn:example:interfaces</namespace></module></modules-state>' MODSTATE='<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"><module-set-id>0</module-set-id><module><name>clixon-lib</name><revision>2020-04-23</revision><namespace>http://clicon.org/lib</namespace></module><module><name>ietf-inet-types</name><revision>2020-07-06</revision><namespace>urn:ietf:params:xml:ns:yang:ietf-inet-types</namespace></module><module><name>ietf-netconf</name><revision>2011-06-01</revision><namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace></module>'
XML='<interfaces xmlns="urn:example:interfaces"><interface><name>e0</name><docs><descr>First interface</descr></docs><type>eth</type><admin-status>up</admin-status><statistics><in-octets>54326.432</in-octets><in-unicast-pkts>8458765</in-unicast-pkts></statistics></interface><interface><name>e1</name><type>eth</type><admin-status>down</admin-status></interface></interfaces>' XML='<interfaces xmlns="urn:example:interfaces"><interface><name>e0</name><docs><descr>First interface</descr></docs><type>eth</type><admin-status>up</admin-status><statistics><in-octets>54326.432</in-octets><in-unicast-pkts>8458765</in-unicast-pkts></statistics></interface><interface><name>e1</name><type>eth</type><admin-status>down</admin-status></interface></interfaces>'
@ -315,12 +315,18 @@ sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "start backend -s startup -f $cfg -q -- -u" new "start backend -s startup -f $cfg -q -- -u"
output=$(sudo $clixon_backend -F -D $DBG -s startup -f $cfg -q -- -u) output=$(sudo $clixon_backend -F -D $DBG -s startup -f $cfg -q -- -u)
#echo "$output"
if [ "$ALL" != "$output" ]; then match=$(echo "$output" | grep --null -o "$MODSTATE")
err "$ALL" "$output" if [ -z "$match" ]; then
err "$MODSTATE" "$output"
fi
match=$(echo "$output" | grep --null -o "$XML")
if [ -z "$match" ]; then
err "$XML" "$output"
fi fi
rm -rf $dir rm -rf $dir

View file

@ -42,10 +42,11 @@ datarootdir = @datarootdir@
# See also OPT_YANG_INSTALLDIR for the standard yang files # See also OPT_YANG_INSTALLDIR for the standard yang files
YANG_INSTALLDIR = @YANG_INSTALLDIR@ YANG_INSTALLDIR = @YANG_INSTALLDIR@
YANGSPECS = clixon-config@2020-10-01.yang YANGSPECS = clixon-config@2020-11-03.yang
YANGSPECS += clixon-lib@2020-04-23.yang YANGSPECS += clixon-lib@2020-04-23.yang
YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang
YANGSPECS += clixon-xml-changelog@2019-03-21.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang
YANGSPECS += clixon-restconf@2020-10-30.yang
APPNAME = clixon # subdir ehere these files are installed APPNAME = clixon # subdir ehere these files are installed

View file

@ -1,803 +0,0 @@
module clixon-config {
yang-version 1.1;
namespace "http://clicon.org/config";
prefix cc;
organization
"Clicon / Clixon";
contact
"Olof Hagsand <olof@hagsand.se>";
description
"Clixon configuration file
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020 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 *****";
/* Deleted: clixon-stats state for clixon XML and memory statistics. (moved to clixon-lib)
*/
revision 2020-06-17 {
description
"Added: CLICON_CLI_LINES_DEFAULT
Added enum HIDE to CLICON_CLI_GENMODEL
Added CLICON_SSL_SERVER_CERT, CLICON_SSL_SERVER_KEY, CLICON_SSL_CA_CERT
Added CLICON_NACM_DISABLED_ON_EMPTY
Removed default valude of CLICON_NACM_RECOVERY_USER";
}
revision 2020-04-23 {
description
"Added: CLICON_YANG_UNKNOWN_ANYDATA to treat unknown XML (wrt YANG) as anydata.
Deleted: xml-stats non-config data (replaced by rpc stats in clixon-lib.yang)";
}
revision 2020-02-22 {
description
"Added: search index extension,
Added: clixon-stats state for clixon XML and memory statistics.
Added: CLICON_CLI_BUF_START and CLICON_CLI_BUF_THRESHOLD for quadratic and linear
growth of CLIgen buffers (cbuf:s)
Added: CLICON_VALIDATE_STATE_XML for controling validation of user state XML
Added: CLICON_CLICON_YANG_LIST_CHECK to skip list key checks";
}
revision 2019-09-11 {
description
"Added: CLICON_BACKEND_USER: drop of privileges to user,
CLICON_BACKEND_PRIVILEGES: how to drop privileges
CLICON_NACM_CREDENTIALS: If and how to check backend sock priveleges with NACM
CLICON_NACM_RECOVERY_USER: Name of NACM recovery user.";
}
revision 2019-06-05 {
description
"Added: CLICON_YANG_REGEXP, CLICON_CLI_TAB_MODE,
CLICON_CLI_HIST_FILE, CLICON_CLI_HIST_SIZE,
CLICON_XML_CHANGELOG, CLICON_XML_CHANGELOG_FILE;
Renamed CLICON_XMLDB_CACHE to CLICON_DATASTORE_CACHE (changed type)
Deleted: CLICON_XMLDB_PLUGIN, CLICON_USE_STARTUP_CONFIG";
}
revision 2019-03-05{
description
"Changed URN. Changed top-level symbol to clixon-config.
Released in Clixon 3.10";
}
revision 2019-02-06 {
description
"Released in Clixon 3.9";
}
revision 2018-10-21 {
description
"Released in Clixon 3.8";
}
extension search_index {
description "This list argument acts as a search index using optimized binary search.
";
}
typedef startup_mode{
description
"Which method to boot/start clicon backend.
The methods differ in how they reach a running state
Which source database to commit from, if any.";
type enumeration{
enum none{
description
"Do not touch running state
Typically after crash when running state and db are synched";
}
enum init{
description
"Initialize running state.
Start with a completely clean running state";
}
enum running{
description
"Commit running db configuration into running state
After reboot if a persistent running db exists";
}
enum startup{
description
"Commit startup configuration into running state
After reboot when no persistent running db exists";
}
}
}
typedef datastore_format{
description
"Datastore format.";
type enumeration{
enum xml{
description "Save and load xmldb as XML";
}
enum json{
description "Save and load xmldb as JSON";
}
}
}
typedef datastore_cache{
description
"XML configuration, ie running/candididate/ datastore cache behaviour.";
type enumeration{
enum nocache{
description "No cache always work directly with file";
}
enum cache{
description "Use in-memory cache.
Make copies when accessing internally.";
}
enum cache-zerocopy{
description "Use in-memory cache and dont copy.
Fastest but opens up for callbacks changing cache.";
}
}
}
typedef cli_genmodel_type{
description
"How to generate CLI from YANG model,
eg {container c {list a{ key x; leaf x; leaf y;}}";
type enumeration{
enum NONE{
description "No extra keywords: c a <x> <y>";
}
enum VARS{
description "Keywords on non-key variables: c a <x> y <y>";
}
enum ALL{
description "Keywords on all variables: c a x <x> y <y>";
}
enum HIDE{
description "Keywords on non-key variables and hide container around lists: a <x> y <y>";
}
}
}
typedef nacm_mode{
description
"Mode of RFC8341 Network Configuration Access Control Model.
It is unclear from the RFC whether NACM rules are internal
in a configuration (ie embedded in regular config) or external/OOB
in s separate, specific NACM-config";
type enumeration{
enum disabled{
description "NACM is disabled";
}
enum internal{
description "NACM is enabled and available in the regular config";
}
enum external{
description "NACM is enabled and available in a separate config";
}
}
}
typedef regexp_mode{
description
"The regular expression engine Clixon uses in its validation of
Yang patterns, and in the CLI.
Yang RFC 7950 stipulates XSD XML Schema regexps
according to W3 CXML Schema Part 2: Datatypes Second Edition,
see http://www.w3.org/TR/2004/REC-xmlschema-2-20041028#regexs";
type enumeration{
enum posix {
description
"Translate XSD XML Schema regexp:s to Posix regexp. This is
not a complete translation, but can be considered good-enough
for Yang use-cases as defined by openconfig and yang-models
for example.";
}
enum libxml2 {
description
"Use libxml2 XSD XML Schema regexp engine. This is a complete
XSD regexp engine..
Requires libxml2 to be available at configure time
(HAVE_LIBXML2 should be set)";
}
}
}
typedef priv_mode{
description
"Privilege mode, used for dropping (or not) priveleges to a non-provileged
user after initialization";
type enumeration{
enum none {
description
"Make no drop/change in privileges.";
}
enum drop_perm {
description
"After initialization, drop privileges permanently to a uid";
}
enum drop_temp {
description
"After initialization, drop privileges temporarily to a euid";
}
}
}
typedef nacm_cred_mode{
description
"How NACM user should be matched with unix socket peer credentials.
This means nacm user must match socket peer user accessing the
backend socket. For IP sockets only mode none makes sense.";
type enumeration{
enum none {
description
"Dont match NACM user to any user credentials. Any user can pose
as any other user. Set this for IP sockets, or dont use NACM.";
}
enum exact {
description
"Exact match between NACM user and unix socket peer user.";
}
enum except {
description
"Exact match between NACM user and unix socket peer user, except
for root and www user (restconf).";
}
}
}
container clixon-config {
leaf-list CLICON_FEATURE {
description
"Supported features as used by YANG feature/if-feature
value is: <module>:<feature>, where <module> and <feature>
are either names, or the special character '*'.
*:* means enable all features
<module>:* means enable all features in the specified module
*:<feature> means enable the specific feature in all modules";
type string;
}
leaf CLICON_CONFIGFILE{
type string;
description
"Location of configuration-file for default values (this file).
Default is CLIXON_DEFAULT_CONFIG=/usr/local/etc/clicon.xml
set in configure. Note that due to bootstrapping, a default
value here does not work.";
}
leaf-list CLICON_YANG_DIR {
ordered-by user;
type string;
description
"Yang directory path for finding module and submodule files.
A list of these options should be in the configuration.
When loading a Yang module, Clixon searches this list in the order
they appear. Ensure that YANG_INSTALLDIR(default
/usr/local/share/clixon) is present in the path";
}
leaf CLICON_YANG_MAIN_FILE {
type string;
description
"If specified load a yang module in a specific absolute filename.
This corresponds to the -y command-line option in most CLixon
programs.";
}
leaf CLICON_YANG_MAIN_DIR {
type string;
description
"If given, load all modules in this directory (all .yang files)
See also CLICON_YANG_DIR which specifies a path of dirs";
}
leaf CLICON_YANG_MODULE_MAIN {
type string;
description
"Option used to construct initial yang file:
<module>[@<revision>]";
}
leaf CLICON_YANG_MODULE_REVISION {
type string;
description
"Option used to construct initial yang file:
<module>[@<revision>].
Used together with CLICON_YANG_MODULE_MAIN";
}
leaf CLICON_YANG_REGEXP {
type regexp_mode;
default posix;
description
"The regular expression engine Clixon uses in its validation of
Yang patterns, and in the CLI.
There is a 'good-enough' posix translation mode and a complete
libxml2 mode";
}
leaf CLICON_YANG_LIST_CHECK {
type boolean;
default true;
description
"If false, skip Yang list check sanity checks from RFC 7950, Sec 7.8.2:
The 'key' statement, which MUST be present if the list represents configuration.
Some yang specs seem not to fulfil this. However, if you reset this, there may
be follow-up errors due to code that assumes a configuration list has keys";
}
leaf CLICON_YANG_UNKNOWN_ANYDATA{
type boolean;
default false;
description
"Treat unknown XML/JSON nodes as anydata when loading from startup db.
This does not apply to namespaces, which means a top-level node: xxx:yyy
is accepted only if yyy is unknown, not xxx.
Note that this option has several caveats which needs to be fixed. Please
use with care.
The primary issue is that the unknown->anydata handling is not restricted to
only loading from startup but may occur in other circumstances as well. This
means that sanity checks of erroneous XML/JSON may not be properly signalled.";
}
leaf CLICON_BACKEND_DIR {
type string;
description
"Location of backend .so plugins. Load all .so
plugins in this dir as backend plugins";
}
leaf CLICON_BACKEND_REGEXP {
type string;
description
"Regexp of matching backend plugins in CLICON_BACKEND_DIR";
default "(.so)$";
}
leaf CLICON_NETCONF_DIR {
type string;
description "Location of netconf (frontend) .so plugins";
}
leaf CLICON_RESTCONF_DIR {
type string;
description
"Location of restconf (frontend) .so plugins. Load all .so
plugins in this dir as restconf code plugins";
}
leaf CLICON_RESTCONF_PATH {
type string;
default "/www-data/fastcgi_restconf.sock";
description
"FastCGI unix socket. Should be specified in webserver
Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock";
}
leaf CLICON_RESTCONF_PRETTY {
type boolean;
default true;
description
"Restconf return value pretty print.
Restconf clients may add HTTP header:
Accept: application/yang-data+json, or
Accept: application/yang-data+xml
to get return value in XML or JSON.
RFC 8040 examples print XML and JSON in pretty-printed form.
Setting this value to false makes restconf return not pretty-printed
which may be desirable for performance or tests";
}
leaf CLICON_SSL_SERVER_CERT {
type string;
default "/etc/ssl/certs/clixon-server-crt.pem";
description
"SSL server cert for restconf https. This is not required if you use
--with-restconf=fcgi, ie a reverse-proxy based such as nginx over fcgi";
}
leaf CLICON_SSL_SERVER_KEY {
type string;
default "/etc/ssl/private/clixon-server-key.pem";
description
"SSL server private key for restconf https. This is not required if you use
--with-restconf=fcgi, ie a reverse-proxy based such as nginx over fcgi";
}
leaf CLICON_SSL_CA_CERT {
type string;
default "/etc/ssl/certs/clixon-ca_crt.pem";
description
"SSL CA cert for client authentication. This is not required if you use
--with-restconf=fcgi, ie a reverse-proxy based such as nginx over fcgi";
}
leaf CLICON_CLI_DIR {
type string;
description
"Directory containing frontend cli loadable plugins. Load all .so
plugins in this directory as CLI object plugins";
}
leaf CLICON_CLISPEC_DIR {
type string;
description
"Directory containing frontend cligen spec files. Load all .cli
files in this directory as CLI specification files.
See also CLICON_CLISPEC_FILE.";
}
leaf CLICON_CLISPEC_FILE {
type string;
description
"Specific frontend cligen spec file as aletrnative or complement
to CLICON_CLISPEC_DIR. Also available as -c in clixon_cli.";
}
leaf CLICON_CLI_MODE {
type string;
default "base";
description
"Startup CLI mode. This should match a CLICON_MODE variable set in
one of the clispec files";
}
leaf CLICON_CLI_GENMODEL {
type int32;
default 1;
description
"0: Do not generate CLISPEC syntax for the auto-cli.
1: Generate a CLI specification for CLI completion of all loaded Yang modules.
This CLI tree can be accessed in CLI-spec files using the tree reference syntax (eg
@datamodel).
2: Same including state syntax in a tree called @datamodelstate.
See also CLICON_CLI_MODEL_TREENAME.";
}
leaf CLICON_CLI_MODEL_TREENAME {
type string;
default "datamodel";
description
"If set, CLI specs can reference the
model syntax using this reference.
Example: set @datamodel, cli_set();
A second tree called eg @datamodelstate is created that
also contains state together with config.";
}
leaf CLICON_CLI_GENMODEL_COMPLETION {
type int32;
default 1;
description "Generate code for CLI completion of existing db symbols.
(consider boolean)";
}
leaf CLICON_CLI_GENMODEL_TYPE {
type cli_genmodel_type;
default "VARS";
description "How to generate and show CLI syntax: VARS|ALL";
}
leaf CLICON_CLI_VARONLY {
type int32;
default 1;
description
"Dont include keys in cvec in cli vars callbacks,
ie a & k in 'a <b> k <c>' ignored
(consider boolean)";
}
leaf CLICON_CLI_LINESCROLLING {
type int32;
default 1;
description
"Set to 0 if you want CLI to wrap to next line.
Set to 1 if you want CLI to scroll sideways when approaching
right margin";
}
leaf CLICON_CLI_LINES_DEFAULT {
type int32;
default 24;
description
"Set to number of CLI terminal rows for pageing/scrolling. 0 means unlimited.
The number is set statically UNLESS:
- there is no terminal, such as file input, in which case nr lines is 0
- there is a terminal sufficiently powerful to read the number of lines from
ioctl calls.
In other words, this setting is used ONLY on raw terminals such as serial
consoles.";
}
leaf CLICON_CLI_TAB_MODE {
type int8;
default 0;
description
"Set CLI tab mode. This is actually a bitfield of three
combinations:
bit 1: 0: <tab> shows short info of available commands
1: <tab> has same output as <?>, ie line per command
bit 2: 0: On <tab>, select a command over a <var> if both exist
1: Commands and vars have same preference.
bit 3: 0: On <tab>, never complete more than one level per <tab>
1: Complete all levels at once if possible.
";
}
leaf CLICON_CLI_UTF8 {
type int8;
default 0;
description
"Set to 1 to enable CLIgen UTF-8 experimental mode.
Note that this feature is EXPERIMENTAL and may not properly handle
scrolling, control characters, etc
(consider boolean)";
}
leaf CLICON_CLI_HIST_FILE {
type string;
default "~/.clixon_cli_history";
description
"Name of CLI history file. If not given, history is not saved.
The number of lines is saved is given by CLICON_CLI_HIST_SIZE.";
}
leaf CLICON_CLI_HIST_SIZE {
type int32;
default 300;
description
"Number of lines to save in CLI history.
Also, if CLICON_CLI_HIST_FILE is set, also the size in lines
of the saved history.";
}
leaf CLICON_CLI_BUF_START {
type uint32;
default 256;
description
"CLIgen buffer (cbuf) initial size.
When the buffer needs to grow, the allocation grows quadratic up to a threshold
after which linear growth continues.
See CLICON_CLI_BUF_THRESHOLD";
}
leaf CLICON_CLI_BUF_THRESHOLD {
type uint32;
default 65536;
description
"CLIgen buffer (cbuf) threshold size.
When the buffer exceeds the threshold, the allocation grows by adding the threshold
value to the buffer length.
If 0, the growth continues with quadratic growth.
See CLICON_CLI_BUF_THRESHOLD";
}
leaf CLICON_SOCK_FAMILY {
type string;
default "UNIX";
description
"Address family for communicating with clixon_backend
(UNIX|IPv4). IPv6 not yet implemented.
Note that UNIX socket makes credential check as follows:
(1) client needs rw access to the socket
(2) NACM credentials can be checked according to CLICON_NACM_CREDENTIALS
Warning: IPv4 and IPv6 sockets have no credential mechanism.
";
}
leaf CLICON_SOCK {
type string;
mandatory true;
description
"If family above is AF_UNIX: Unix socket for communicating
with clixon_backend. If family is AF_INET: IPv4 address";
}
leaf CLICON_SOCK_PORT {
type int32;
default 4535;
description
"Inet socket port for communicating with clixon_backend
(only IPv4|IPv6)";
}
leaf CLICON_SOCK_GROUP {
type string;
default "clicon";
description
"Group membership to access clixon_backend unix socket and gid for
deamon";
}
leaf CLICON_BACKEND_USER {
type string;
description
"User name for backend (both foreground and daemonized).
If you set this value the backend if started as root will lower
the privileges after initialization.
The ownership of files created by the backend will also be set to this
user (eg datastores).
It also sets the backend unix socket owner to this user, but its group
is set by CLICON_SOCK_GROUP.
See also CLICON_PRIVILEGES setting";
}
leaf CLICON_BACKEND_PRIVILEGES {
type priv_mode;
default none;
description
"Backend privileges mode.
If CLICON_BACKEND_USER user is set, mode can be set to drop_perm or
drop_temp.";
}
leaf CLICON_BACKEND_PIDFILE {
type string;
mandatory true;
description "Process-id file of backend daemon";
}
leaf CLICON_AUTOCOMMIT {
type int32;
default 0;
description
"Set if all configuration changes are committed automatically
on every edit change. Explicit commit commands unnecessary
(consider boolean)";
}
leaf CLICON_XMLDB_DIR {
type string;
mandatory true;
description
"Directory where \"running\", \"candidate\" and \"startup\" are placed.";
}
leaf CLICON_DATASTORE_CACHE {
type datastore_cache;
default cache;
description
"Clixon datastore cache behaviour. There are three values: no cache,
cache with copy, or cache without copy.";
}
leaf CLICON_XMLDB_FORMAT {
type datastore_format;
default xml;
description "XMLDB datastore format.";
}
leaf CLICON_XMLDB_PRETTY {
type boolean;
default true;
description
"XMLDB datastore pretty print.
If set, insert spaces and line-feeds making the XML/JSON human
readable. If not set, make the XML/JSON more compact.";
}
leaf CLICON_XMLDB_MODSTATE {
type boolean;
default false;
description
"If set, tag datastores with RFC 7895 YANG Module Library
info. When loaded at startup, a check is made if the system
yang modules match.
See also CLICON_MODULE_LIBRARY_RFC7895";
}
leaf CLICON_XML_CHANGELOG {
type boolean;
default false;
description "If true enable automatic upgrade using yang clixon
changelog.";
}
leaf CLICON_XML_CHANGELOG_FILE {
type string;
description "Name of file with module revision changelog.
If CLICON_XML_CHANGELOG is true, Clixon
reads the module changelog from this file.";
}
leaf CLICON_VALIDATE_STATE_XML {
type boolean;
default false;
description
"Validate user state callback content.
Users may register state callbacks using ca_statedata callback
When set, the XML returned from the callback is validated after merging with
the running db. If it fails, an internal error is returned to the originating
user.
If the option is not set, the XML returned by the user is not validated.
Note that enabling currently causes a large performance overhead for large
lists, therefore it is recommended to enable it during development and debugging
but disable it in production, until this has been resolved.";
}
leaf CLICON_STARTUP_MODE {
type startup_mode;
description "Which method to boot/start clicon backend";
}
leaf CLICON_TRANSACTION_MOD {
type boolean;
default false;
description "If set, modifications in validation and commit
callbacks are written back into the datastore.
This is a bad idea and therefore obsoleted.";
status obsolete;
}
leaf CLICON_NACM_MODE {
type nacm_mode;
default disabled;
description
"RFC8341 network access configuration control model (NACM) mode: disabled,
in regular (internal) config or separate external file given by CLICON_NACM_FILE";
}
leaf CLICON_NACM_FILE {
type string;
description
"RFC8341 NACM external configuration file (if CLIXON_NACM_MODE is external)";
}
leaf CLICON_NACM_CREDENTIALS {
type nacm_cred_mode;
default except;
description
"Verify nacm user credentials with unix socket peer cred.
This means nacm user must match unix user accessing the backend
socket.";
}
leaf CLICON_NACM_RECOVERY_USER {
type string;
description
"RFC8341 defines a 'recovery session' as outside its scope. Clixon
defines this user as having special admin rights to exempt from
all access control enforcements.
Note setting of CLICON_NACM_CREDENTIALS is important, if set to
exact for example, this user must exist and be used, otherwise
another user (such as root or www) can pose as the recovery user.";
}
leaf CLICON_NACM_DISABLED_ON_EMPTY {
type boolean;
default false;
description
"RFC 8341 and ietf-netconf-acm@2018-02-14.yang defines enable-nacm as true by
default. Since also write-default is deny by default it leads to that empty
configs can not be edited.
This means that a startup config must always have a NACM configuration or
that the NACM recovery session is used to edit an empty config.
If this option is set, Clixon disables NACM if a datastore is empty on load.
Note that it only makes the check on initial load, not if a store 'becomes'
empty, but enables a clixon nacm system to start empty and add an NACM
config after boot.";
}
leaf CLICON_MODULE_LIBRARY_RFC7895 {
type boolean;
default true;
description
"Enable RFC 7895 YANG Module library support as state data. If
enabled, module info will appear when doing netconf get or
restconf GET.
See also CLICON_XMLDB_MODSTATE";
}
leaf CLICON_MODULE_SET_ID {
type string;
default "0";
description "If RFC 7895 YANG Module library enabled:
Contains a server-specific identifier representing
the current set of modules and submodules. The
server MUST change the value of this leaf if the
information represented by the 'module' list instances
has changed.";
}
leaf CLICON_STREAM_DISCOVERY_RFC5277 {
type boolean;
default false;
description "Enable event stream discovery as described in RFC 5277
sections 3.2. If enabled, available streams will appear
when doing netconf get or restconf GET";
}
leaf CLICON_STREAM_DISCOVERY_RFC8040 {
type boolean;
default false;
description
"Enable monitoring information for the RESTCONF protocol from RFC 8040";
}
leaf CLICON_STREAM_PATH {
type string;
default "streams";
description "Stream path appended to CLICON_STREAM_URL to form
stream subscription URL.";
}
leaf CLICON_STREAM_URL {
type string;
default "https://localhost";
description "Prepend this to CLICON_STREAM_PATH to form URL.
See RFC 8040 Sec 9.3 location leaf:
'Contains a URL that represents the entry point for
establishing notification delivery via server-sent events.'
Prepend this constant to name of stream.
Example: https://localhost/streams/NETCONF. Note this is the
external URL, not local behind a reverse-proxy.
Note that -s <stream> command-line option to clixon_restconf
should correspond to last path of url (eg 'streams')";
}
leaf CLICON_STREAM_PUB {
type string;
description "For stream publish using eg nchan, the base address
to publish to. Example value: http://localhost/pub
Example: stream NETCONF would then be pushed to
http://localhost/pub/NETCONF.
Note this may be a local/provate URL behind reverse-proxy.
If not given, do NOT enable stream publishing using NCHAN.";
}
leaf CLICON_STREAM_RETENTION {
type uint32;
default 3600;
units s;
description "Retention for stream replay buffers in seconds, ie how much
data to store before dropping. 0 means no retention";
}
}
}

View file

@ -40,8 +40,14 @@ module clixon-config {
***** END LICENSE BLOCK *****"; ***** END LICENSE BLOCK *****";
/* Deleted: clixon-stats state for clixon XML and memory statistics. (moved to clixon-lib) revision 2020-11-03 {
*/ description
"Added: CLICON_RESTCONF_CONFIG";
}
revision 2020-10-01 {
description
"Added: CLICON_CONFIGDIR.";
}
revision 2020-08-17 { revision 2020-08-17 {
description description
"Added: CLICON_RESTCONF_IPV4_ADDR, CLICON_RESTCONF_IPV6_ADDR, "Added: CLICON_RESTCONF_IPV4_ADDR, CLICON_RESTCONF_IPV6_ADDR,
@ -274,14 +280,6 @@ module clixon-config {
*:<feature> means enable the specific feature in all modules"; *:<feature> means enable the specific feature in all modules";
type string; type string;
} }
leaf CLICON_CONFIGFILE{
type string;
description
"Location of configuration-file for default values (this file).
Default is CLIXON_DEFAULT_CONFIG=/usr/local/etc/clicon.xml
set in configure. Note that due to bootstrapping, a default
value here does not work.";
}
leaf-list CLICON_YANG_DIR { leaf-list CLICON_YANG_DIR {
ordered-by user; ordered-by user;
type string; type string;
@ -292,6 +290,28 @@ module clixon-config {
they appear. Ensure that YANG_INSTALLDIR(default they appear. Ensure that YANG_INSTALLDIR(default
/usr/local/share/clixon) is present in the path"; /usr/local/share/clixon) is present in the path";
} }
leaf CLICON_CONFIGFILE{
type string;
description
"Location of the main configuration-file.
Default is CLIXON_DEFAULT_CONFIG=/usr/local/etc/clicon.xml set in configure.
Note that due to bootstrapping, this value is not actually read from file
and therefore a default value would be meaningless.";
}
leaf CLICON_CONFIGDIR{
type string;
description
"Location of directory of extra configuration files.
If not given, only main configfile is read.
If given, and if the directory exists, all files in this directory will be loaded
AFTER the main config file (CLICON_CONFIGFILE) in the following way:
- leaf values are overwritten
- leaf-list values are appended
The files in this directory will be loaded alphabetically.
If the dir is given but does not exist will result in an error.
You can override file setting with -E <dir> command-line option.
Note that due to bootstraping this value is only meaningful in the main config file";
}
leaf CLICON_YANG_MAIN_FILE { leaf CLICON_YANG_MAIN_FILE {
type string; type string;
description description
@ -376,7 +396,8 @@ module clixon-config {
default "/www-data/fastcgi_restconf.sock"; default "/www-data/fastcgi_restconf.sock";
description description
"FastCGI unix socket. Should be specified in webserver "FastCGI unix socket. Should be specified in webserver
Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock"; Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock
Only if with-restconf=fcgi, NOT evhtp";
} }
leaf CLICON_RESTCONF_PRETTY { leaf CLICON_RESTCONF_PRETTY {
type boolean; type boolean;
@ -391,19 +412,40 @@ module clixon-config {
Setting this value to false makes restconf return not pretty-printed Setting this value to false makes restconf return not pretty-printed
which may be desirable for performance or tests"; which may be desirable for performance or tests";
} }
leaf CLICON_RESTCONF_CONFIG {
type boolean;
default false;
description
"If set, load clixon-restconf module automatically. This also means
that the clixon restconf daemon gets its restconf-specific configuration
from that yang loaded in the backend running datastore instead of from
the clixon XML config file.
This only works with-restconf=evhtp, NOT with restconf=fcgi (nginx)
A consequence is that if set, the following option in this YANG are obsolete:
CLICON_RESTCONF_IPV4_ADDR
CLICON_RESTCONF_IPV6_ADDR
CLICON_RESTCONF_HTTP_PORT
CLICON_RESTCONF_HTTPS_PORT
CLICON_SSL_SERVER_CERT
CLICON_SSL_SERVER_KEY
CLICON_SSL_CA_CERT
";
}
leaf CLICON_RESTCONF_IPV4_ADDR { leaf CLICON_RESTCONF_IPV4_ADDR {
type string; type string;
default "0.0.0.0"; default "0.0.0.0";
description description
"RESTCONF IPv4 socket binding address. "RESTCONF IPv4 socket binding address.
Applies to native http by config option --with-restconf=evhtp."; Applies to native http by config option --with-restconf=evhtp.
Obsolete if CLICON_RESTCONF_CONFIG is true";
} }
leaf CLICON_RESTCONF_IPV6_ADDR { leaf CLICON_RESTCONF_IPV6_ADDR {
type string; type string;
default "::"; default "::";
description description
"RESTCONF IPv6 socket binding address. "RESTCONF IPv6 socket binding address.
Applies to native http by config option --with-restconf=evhtp."; Applies to native http by config option --with-restconf=evhtp.
Obsolete if CLICON_RESTCONF_CONFIG is true";
} }
leaf CLICON_RESTCONF_HTTP_PORT { leaf CLICON_RESTCONF_HTTP_PORT {
type uint16; type uint16;
@ -411,7 +453,8 @@ module clixon-config {
description description
"RESTCONF socket binding port, non-ssl "RESTCONF socket binding port, non-ssl
In the restconf daemon, it can be overriden by -P <port> In the restconf daemon, it can be overriden by -P <port>
Applies to native http only by config option --with-restconf=evhtp."; Applies to native http only by config option --with-restconf=evhtp.
Obsolete if CLICON_RESTCONF_CONFIG is true";
} }
leaf CLICON_RESTCONF_HTTPS_PORT { leaf CLICON_RESTCONF_HTTPS_PORT {
type uint16; type uint16;
@ -420,28 +463,32 @@ module clixon-config {
"RESTCONF socket binding port, ssl "RESTCONF socket binding port, ssl
In the restconf daemon, this is the port chosen if -s is given. In the restconf daemon, this is the port chosen if -s is given.
Note it can be overriden by -P <port> Note it can be overriden by -P <port>
Applies to native http by config option --with-restconf=evhtp."; Applies to native http by config option --with-restconf=evhtp.
Obsolete if CLICON_RESTCONF_CONFIG is true";
} }
leaf CLICON_SSL_SERVER_CERT { leaf CLICON_SSL_SERVER_CERT {
type string; type string;
default "/etc/ssl/certs/clixon-server-crt.pem"; default "/etc/ssl/certs/clixon-server-crt.pem";
description description
"SSL server cert for restconf https. "SSL server cert for restconf https.
Applies to native http only by config option --with-restconf=evhtp."; Applies to native http only by config option --with-restconf=evhtp.
Obsolete if CLICON_RESTCONF_CONFIG is true";
} }
leaf CLICON_SSL_SERVER_KEY { leaf CLICON_SSL_SERVER_KEY {
type string; type string;
default "/etc/ssl/private/clixon-server-key.pem"; default "/etc/ssl/private/clixon-server-key.pem";
description description
"SSL server private key for restconf https. "SSL server private key for restconf https.
Applies to native http only by config option --with-restconf=evhtp."; Applies to native http only by config option --with-restconf=evhtp.
Obsolete if CLICON_RESTCONF_CONFIG is true";
} }
leaf CLICON_SSL_CA_CERT { leaf CLICON_SSL_CA_CERT {
type string; type string;
default "/etc/ssl/certs/clixon-ca_crt.pem"; default "/etc/ssl/certs/clixon-ca_crt.pem";
description description
"SSL CA cert for client authentication. "SSL CA cert for client authentication.
Applies to native http only by config option --with-restconf=evhtp."; Applies to native http only by config option --with-restconf=evhtp.
Obsolete if CLICON_RESTCONF_CONFIG is true";
} }
leaf CLICON_CLI_DIR { leaf CLICON_CLI_DIR {
type string; type string;
@ -477,18 +524,23 @@ module clixon-config {
1: Generate a CLI specification for CLI completion of all loaded Yang modules. 1: Generate a CLI specification for CLI completion of all loaded Yang modules.
This CLI tree can be accessed in CLI-spec files using the tree reference syntax (eg This CLI tree can be accessed in CLI-spec files using the tree reference syntax (eg
@datamodel). @datamodel).
2: Same including state syntax in a tree called @datamodelstate. 2: Same including state syntax in a tree called @datamodelstate and @datamodelshow
See also CLICON_CLI_MODEL_TREENAME."; See also CLICON_CLI_MODEL_TREENAME.";
} }
leaf CLICON_CLI_MODEL_TREENAME { leaf CLICON_CLI_MODEL_TREENAME {
type string; type string;
default "datamodel"; default "datamodel";
description description
"If set, CLI specs can reference the "If CLICON_CLI_GENMOEL is set, CLI specs can reference the
model syntax using this reference. model syntax using a model tree set by this option.
Three trees are generated with this name as a base, (assuming base is datamodel):
- @datamodel - a clispec for navigating in editing a configuration (set/merge/delete)
- @datamodelshow - a clispec for navigating in showing a configuration
- @datamodelstate - a clispec for navigating in showing a configuration WITH state
Example: set @datamodel, cli_set(); Example: set @datamodel, cli_set();
A second tree called eg @datamodelstate is created that show @datamodelshow, cli_show_auto();
also contains state together with config."; show state @datamodelstate, cli_show_auto_state();
";
} }
leaf CLICON_CLI_GENMODEL_COMPLETION { leaf CLICON_CLI_GENMODEL_COMPLETION {
type int32; type int32;

View file

@ -0,0 +1,190 @@
module clixon-restconf {
yang-version 1.1;
namespace "https://clicon.org/restconf";
prefix "clrc";
import ietf-inet-types {
prefix inet;
}
organization
"Clixon";
contact
"Olof Hagsand <olof@hagsand.se>";
description
"This YANG module provides a data-model for the Clixon RESTCONF daemon.
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2020 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 *****";
revision 2020-10-30 {
description
"Initial release";
}
typedef http-auth-type {
type enumeration {
enum client-certificate {
description
"TLS client certificate";
}
enum password {
description
"PAM password auth";
}
enum none {
description
"No authentication, no security.";
}
}
description
"Enumeration of HTTP authorization types.";
}
typedef service-operation {
type enumeration {
enum start {
description
"Start if not already running";
}
enum stop {
description
"Stop if running";
}
enum restart {
description
"Stop if running, then start";
}
enum status {
description
"Check status";
}
}
description
"Common operations that can be performed on a service";
}
container restconf {
presence "Enables RESTCONF";
description
"HTTP daemon configuration.";
list socket {
key "namespace address port";
leaf namespace {
type string;
description "indicates a namespace for instance. On platforms where namespaces are not suppported, always 'default'";
}
leaf address {
type inet:ip-address;
description "IP address to bind to";
}
leaf port {
type inet:port-number;
description "IP port to bind to";
}
leaf ssl {
type boolean;
default true;
description "Enable for HTTPS otherwise HTTP protocol";
}
}
leaf auth-type {
type http-auth-type;
description
"The authentication type.
Note client-certificate applies only if socket has ssl enabled";
}
leaf server-cert-path {
type string;
description
"Path to server certificate file.
Note only applies if socket has ssl enabled";
default "/etc/ssl/private/clixon-server-crt.pem";
/* See CLICON_SSL_SERVER_CERT */
}
leaf server-key-path {
type string;
description
"Path to server key file
Note only applies if socket has ssl enabled";
default "/etc/ssl/private/clixon-server-key.pem";
/* See CLICON_SSL_SERVER_KEY */
}
leaf server-ca-cert-path {
type string;
description
"Path to server CA cert file
Note only applies if socket has ssl enabled";
default "/etc/ssl/certs/clixon-ca_crt.pem";
/* CLICON_SSL_CA_CERT */
}
leaf client-cert-ca {
type string;
description
"Path to client certificate file
Note only applies if socket has ssl enabled AND auth-type has client-certificate
XXX: Is this relevant?";
}
}
rpc restconf-control {
input {
leaf operation {
type service-operation;
mandatory true;
description
"One of the strings 'start', 'stop', 'restart', or 'status'.";
}
leaf namespace {
type string;
description
"Network namespace.";
}
}
output {
leaf stdout {
type string;
}
}
}
rpc restconf-coredump {
input {
leaf operation {
type boolean;
mandatory true;
}
leaf namespace {
type string;
description
"Network namespace.";
}
}
output {
leaf stdout {
type string;
}
}
}
}

View file

@ -41,7 +41,7 @@ datarootdir = @datarootdir@
# See also YANG_INSTALLDIR for the clixon-specific yang files # See also YANG_INSTALLDIR for the clixon-specific yang files
YANG_INSTALLDIR = @YANG_INSTALLDIR@ YANG_INSTALLDIR = @YANG_INSTALLDIR@
YANGSPECS = ietf-inet-types@2013-07-15.yang YANGSPECS = ietf-inet-types@2020-07-06.yang
YANGSPECS += ietf-netconf@2011-06-01.yang YANGSPECS += ietf-netconf@2011-06-01.yang
YANGSPECS += ietf-netconf-acm@2018-02-14.yang YANGSPECS += ietf-netconf-acm@2018-02-14.yang
YANGSPECS += ietf-restconf@2017-01-26.yang YANGSPECS += ietf-restconf@2017-01-26.yang

View file

@ -0,0 +1,589 @@
module ietf-inet-types {
namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
prefix "inet";
organization
"IETF Network Modeling (NETMOD) Working Group";
contact
"WG Web: <https://datatracker.ietf.org/wg/netmod/>
WG List: <mailto:netmod@ietf.org>
Editor: Juergen Schoenwaelder
<mailto:j.schoenwaelder@jacobs-university.de>";
description
"This module contains a collection of generally useful derived
YANG data types for Internet addresses and related things.
The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL
NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED',
'MAY', and 'OPTIONAL' in this document are to be interpreted as
described in BCP 14 (RFC 2119) (RFC 8174) when, and only when,
they appear in all capitals, as shown here.
Copyright (c) 2020 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD License
set forth in Section 4.c of the IETF Trust's Legal Provisions
Relating to IETF Documents
(http://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC XXXX;
see the RFC itself for full legal notices.";
revision 2020-07-06 {
description
"This revision adds the following new data types:
- ip-address-and-prefix
- ipv4-address-and-prefix
- ipv6-address-and-prefix
- email-address";
reference
"RFC XXXX: Common YANG Data Types";
}
revision 2013-07-15 {
description
"This revision adds the following new data types:
- ip-address-no-zone
- ipv4-address-no-zone
- ipv6-address-no-zone";
reference
"RFC 6991: Common YANG Data Types";
}
revision 2010-09-24 {
description
"Initial revision.";
reference
"RFC 6021: Common YANG Data Types";
}
/*** collection of types related to protocol fields ***/
typedef ip-version {
type enumeration {
enum unknown {
value "0";
description
"An unknown or unspecified version of the Internet
protocol.";
}
enum ipv4 {
value "1";
description
"The IPv4 protocol as defined in RFC 791.";
}
enum ipv6 {
value "2";
description
"The IPv6 protocol as defined in RFC 2460.";
}
}
description
"This value represents the version of the IP protocol.
In the value set and its semantics, this type is equivalent
to the InetVersion textual convention of the SMIv2.";
reference
"RFC 791: Internet Protocol
RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
RFC 4001: Textual Conventions for Internet Network Addresses";
}
typedef dscp {
type uint8 {
range "0..63";
}
description
"The dscp type represents a Differentiated Services Code Point
that may be used for marking packets in a traffic stream.
In the value set and its semantics, this type is equivalent
to the Dscp textual convention of the SMIv2.";
reference
"RFC 3289: Management Information Base for the Differentiated
Services Architecture
RFC 2474: Definition of the Differentiated Services Field
(DS Field) in the IPv4 and IPv6 Headers
RFC 2780: IANA Allocation Guidelines For Values In
the Internet Protocol and Related Headers";
}
typedef ipv6-flow-label {
type uint32 {
range "0..1048575";
}
description
"The ipv6-flow-label type represents the flow identifier or
Flow Label in an IPv6 packet header that may be used to
discriminate traffic flows.
In the value set and its semantics, this type is equivalent
to the IPv6FlowLabel textual convention of the SMIv2.";
reference
"RFC 3595: Textual Conventions for IPv6 Flow Label
RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
}
typedef port-number {
type uint16 {
range "0..65535";
}
description
"The port-number type represents a 16-bit port number of an
Internet transport-layer protocol such as UDP, TCP, DCCP, or
SCTP. Port numbers are assigned by IANA. A current list of
all assignments is available from <http://www.iana.org/>.
Note that the port number value zero is reserved by IANA. In
situations where the value zero does not make sense, it can
be excluded by subtyping the port-number type.
In the value set and its semantics, this type is equivalent
to the InetPortNumber textual convention of the SMIv2.";
reference
"RFC 768: User Datagram Protocol
RFC 793: Transmission Control Protocol
RFC 4960: Stream Control Transmission Protocol
RFC 4340: Datagram Congestion Control Protocol (DCCP)
RFC 4001: Textual Conventions for Internet Network Addresses";
}
/*** collection of types related to autonomous systems ***/
typedef as-number {
type uint32;
description
"The as-number type represents autonomous system numbers
which identify an Autonomous System (AS). An AS is a set
of routers under a single technical administration, using
an interior gateway protocol and common metrics to route
packets within the AS, and using an exterior gateway
protocol to route packets to other ASes. IANA maintains
the AS number space and has delegated large parts to the
regional registries.
Autonomous system numbers were originally limited to 16
bits. BGP extensions have enlarged the autonomous system
number space to 32 bits. This type therefore uses an uint32
base type without a range restriction in order to support
a larger autonomous system number space.
In the value set and its semantics, this type is equivalent
to the InetAutonomousSystemNumber textual convention of
the SMIv2.";
reference
"RFC 1930: Guidelines for creation, selection, and registration
of an Autonomous System (AS)
RFC 4271: A Border Gateway Protocol 4 (BGP-4)
RFC 4001: Textual Conventions for Internet Network Addresses
RFC 6793: BGP Support for Four-Octet Autonomous System (AS)
Number Space";
}
/*** collection of types related to IP addresses and hostnames ***/
typedef ip-address {
type union {
type inet:ipv4-address;
type inet:ipv6-address;
}
description
"The ip-address type represents an IP address and is IP
version neutral. The format of the textual representation
implies the IP version. This type supports scoped addresses
by allowing zone identifiers in the address format.";
reference
"RFC 4007: IPv6 Scoped Address Architecture";
}
typedef ipv4-address {
type string {
pattern
'(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ '(%[\p{N}\p{L}]+)?';
}
description
"The ipv4-address type represents an IPv4 address in
dotted-quad notation. The IPv4 address may include a zone
index, separated by a % sign.
The zone index is used to disambiguate identical address
values. For link-local addresses, the zone index will
typically be the interface index number or the name of an
interface. If the zone index is not present, the default
zone of the device will be used.
The canonical format for the zone index is the numerical
format";
}
typedef ipv6-address {
type string {
pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+ '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+ '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+ '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+ '(%[\p{N}\p{L}]+)?';
pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+ '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+ '(%.+)?';
}
description
"The ipv6-address type represents an IPv6 address in full,
mixed, shortened, and shortened-mixed notation. The IPv6
address may include a zone index, separated by a % sign.
The zone index is used to disambiguate identical address
values. For link-local addresses, the zone index will
typically be the interface index number or the name of an
interface. If the zone index is not present, the default
zone of the device will be used.
The canonical format of IPv6 addresses uses the textual
representation defined in Section 4 of RFC 5952. The
canonical format for the zone index is the numerical
format as described in Section 11.2 of RFC 4007.";
reference
"RFC 4291: IP Version 6 Addressing Architecture
RFC 4007: IPv6 Scoped Address Architecture
RFC 5952: A Recommendation for IPv6 Address Text
Representation";
}
typedef ip-address-no-zone {
type union {
type inet:ipv4-address-no-zone;
type inet:ipv6-address-no-zone;
}
description
"The ip-address-no-zone type represents an IP address and is
IP version neutral. The format of the textual representation
implies the IP version. This type does not support scoped
addresses since it does not allow zone identifiers in the
address format.";
reference
"RFC 4007: IPv6 Scoped Address Architecture";
}
typedef ipv4-address-no-zone {
type inet:ipv4-address {
pattern '[0-9\.]*';
}
description
"An IPv4 address without a zone index. This type, derived from
ipv4-address, may be used in situations where the zone is known
from the context and hence no zone index is needed.";
}
typedef ipv6-address-no-zone {
type inet:ipv6-address {
pattern '[0-9a-fA-F:\.]*';
}
description
"An IPv6 address without a zone index. This type, derived from
ipv6-address, may be used in situations where the zone is known
from the context and hence no zone index is needed.";
reference
"RFC 4291: IP Version 6 Addressing Architecture
RFC 4007: IPv6 Scoped Address Architecture
RFC 5952: A Recommendation for IPv6 Address Text
Representation";
}
typedef ip-prefix {
type union {
type inet:ipv4-prefix;
type inet:ipv6-prefix;
}
description
"The ip-prefix type represents an IP prefix and is IP
version neutral. The format of the textual representations
implies the IP version.";
}
typedef ipv4-prefix {
type string {
pattern
'(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ '/(([0-9])|([1-2][0-9])|(3[0-2]))';
}
description
"The ipv4-prefix type represents an IPv4 prefix.
The prefix length is given by the number following the
slash character and must be less than or equal to 32.
A prefix length value of n corresponds to an IP address
mask that has n contiguous 1-bits from the most
significant bit (MSB) and all other bits set to 0.
The canonical format of an IPv4 prefix has all bits of
the IPv4 address set to zero that are not part of the
IPv4 prefix.
The definition of ipv4-prefix does not require that bits,
which are not part of the prefix, are set to zero. However,
implementations have to return values in canonical format,
which requires non-prefix bits to be set to zero. This means
that 192.0.2.1/24 must be accepted as a valid value but it
will be converted into the canonical format 192.0.2.0/24.";
}
typedef ipv6-prefix {
type string {
pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+ '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+ '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+ '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+ '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+ '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+ '(/.+)';
}
description
"The ipv6-prefix type represents an IPv6 prefix.
The prefix length is given by the number following the
slash character and must be less than or equal to 128.
A prefix length value of n corresponds to an IP address
mask that has n contiguous 1-bits from the most
significant bit (MSB) and all other bits set to 0.
The canonical format of an IPv6 prefix has all bits of
the IPv6 address set to zero that are not part of the
IPv6 prefix. Furthermore, the IPv6 address is represented
as defined in Section 4 of RFC 5952.
The definition of ipv6-prefix does not require that bits,
which are not part of the prefix, are set to zero. However,
implementations have to return values in canonical format,
which requires non-prefix bits to be set to zero. This means
that 2001:db8::1/64 must be accepted as a valid value but it
will be converted into the canonical format 2001:db8::/64.";
reference
"RFC 5952: A Recommendation for IPv6 Address Text
Representation";
}
typedef ip-address-and-prefix {
type union {
type inet:ipv4-address-and-prefix;
type inet:ipv6-address-and-prefix;
}
description
"The ip-address-and-prefix type represents an IP address and
prefix and is IP version neutral. The format of the textual
representations implies the IP version.";
}
typedef ipv4-address-and-prefix {
type string {
pattern
'(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ '/(([0-9])|([1-2][0-9])|(3[0-2]))';
}
description
"The ipv4-address-and-prefix type represents an IPv4
address and an associated ipv4 prefix.
The prefix length is given by the number following the
slash character and must be less than or equal to 32.
A prefix length value of n corresponds to an IP address
mask that has n contiguous 1-bits from the most
significant bit (MSB) and all other bits set to 0.";
}
typedef ipv6-address-and-prefix {
type string {
pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+ '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+ '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+ '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+ '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+ '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+ '(/.+)';
}
description
"The ipv6-address-and-prefix type represents an IPv6
address and an associated ipv4 prefix.
The prefix length is given by the number following the
slash character and must be less than or equal to 128.
A prefix length value of n corresponds to an IP address
mask that has n contiguous 1-bits from the most
significant bit (MSB) and all other bits set to 0.
The canonical format requires that the IPv6 address is
represented as defined in Section 4 of RFC 5952.";
reference
"RFC 5952: A Recommendation for IPv6 Address Text
Representation";
}
/*** collection of domain name and URI types ***/
typedef domain-name {
type string {
length "1..253";
pattern
'((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+ '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+ '|\.';
}
description
"The domain-name type represents a DNS domain name. The
name SHOULD be fully qualified whenever possible. This
type does not support wildcards (see RFC 4592) or
classless in-addr.arpa delegations (see RFC 2317).
Internet domain names are only loosely specified. Section
3.5 of RFC 1034 recommends a syntax (modified in Section
2.1 of RFC 1123). The pattern above is intended to allow
for current practice in domain name use, and some possible
future expansion. Note that Internet host names have a
stricter syntax (described in RFC 952) than the DNS
recommendations in RFCs 1034 and 1123, and that systems
that want to store host names in schema node instances
using the domain-name type are recommended to adhere to
this stricter standard to ensure interoperability.
The encoding of DNS names in the DNS protocol is limited
to 255 characters. Since the encoding consists of labels
prefixed by a length bytes and there is a trailing NULL
byte, only 253 characters can appear in the textual dotted
notation.
The description clause of schema nodes using the domain-name
type MUST describe when and how these names are resolved to
IP addresses. Note that the resolution of a domain-name value
may require to query multiple DNS records (e.g., A for IPv4
and AAAA for IPv6). The order of the resolution process and
which DNS record takes precedence can either be defined
explicitly or may depend on the configuration of the
resolver.
Domain-name values use the US-ASCII encoding. Their canonical
format uses lowercase US-ASCII characters. Internationalized
domain names MUST be A-labels as per RFC 5890.";
reference
"RFC 952: DoD Internet Host Table Specification
RFC 1034: Domain Names - Concepts and Facilities
RFC 1123: Requirements for Internet Hosts -- Application
and Support
RFC 2317: Classless IN-ADDR.ARPA delegation
RFC 2782: A DNS RR for specifying the location of services
(DNS SRV)
RFC 4592: The Role of Wildcards in the Domain Name System
RFC 5890: Internationalized Domain Names in Applications
(IDNA): Definitions and Document Framework";
}
typedef host {
type union {
type inet:ip-address;
type inet:domain-name;
}
description
"The host type represents either an IP address or a DNS
domain name.";
}
/*
* DISCUSS:
* - Lada suggested to replace the inet:domain-name usage in
* the union with a new host-name definition that follows
* the NR-LDH definition in RFC 5890.
*/
typedef uri {
type string;
description
"The uri type represents a Uniform Resource Identifier
(URI) as defined by STD 66.
Objects using the uri type MUST be in US-ASCII encoding,
and MUST be normalized as described by RFC 3986 Sections
6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary
percent-encoding is removed, and all case-insensitive
characters are set to lowercase except for hexadecimal
digits, which are normalized to uppercase as described in
Section 6.2.2.1.
The purpose of this normalization is to help provide
unique URIs. Note that this normalization is not
sufficient to provide uniqueness. Two URIs that are
textually distinct after this normalization may still be
equivalent.
Objects using the uri type may restrict the schemes that
they permit. For example, 'data:' and 'urn:' schemes
might not be appropriate.
A zero-length URI is not a valid URI. This can be used to
express 'URI absent' where required.
In the value set and its semantics, this type is equivalent
to the Uri SMIv2 textual convention defined in RFC 5017.";
reference
"RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
Group: Uniform Resource Identifiers (URIs), URLs,
and Uniform Resource Names (URNs): Clarifications
and Recommendations
RFC 5017: MIB Textual Conventions for Uniform Resource
Identifiers (URIs)";
}
typedef email-address {
type string {
// dot-atom-text "@" ...
pattern '[a-zA-Z0-9!#$%&'+"'"+'*+/=?^_`{|}~-]+'
+ '(\.[a-zA-Z0-9!#$%&'+"'"+'*+/=?^_`{|}~-]+)*'
+ '@'
+ '[a-zA-Z0-9!#$%&'+"'"+'*+/=?^_`{|}~-]+'
+ '(\.[a-zA-Z0-9!#$%&'+"'"+'*+/=?^_`{|}~-]+)*';
}
description
"The email-address type represents an email address as
defined as addr-spec in RFC 5322 section 3.4.1.";
reference
"RFC 5322: Internet Message Format";
}
/*
* DISCUSS:
* - It was suggested to add email types following RFC 5322
* email-address (addr-spec, per Section 3.4.1)
* named-email-address (name-addr, per Section 3.4)
* - This sounds useful but the devil is in the details,
* in particular name-addr is a quite complex construct;
* perhaps addr-spec is sufficient, this is also the
* format allowed in mailto: URIs (mailto: seems to use
* only a subset of addr-spec which may be good enough
* here as well).
* - Need to define a pattern that has a meaningful trade-off
* between precision and complexity (there are very tight
* pattern that are very long and complex). The current
* pattern does not take care of quoted-string, obs-local-part,
* domain-literal, obs-domain.
*/
/*
* DISCUSS:
* - There was a request to add types for URI fields (scheme,
* authority, path, query, fragment) but it is not clear how
* commonly useful these types are, the WG was pretty silent
* about this proposal. On the technical side, it is unclear
* whether data is represented with percent escapes resolved
* or not. (Mahesh's proposal does not spell this out, the
* pattern does not allow the % character, which may be wrong.)
*/
}