Restconf callhome: Added YANG connection-type, test: added NACM
This commit is contained in:
parent
9de052c144
commit
7e783952eb
9 changed files with 194 additions and 82 deletions
|
|
@ -42,6 +42,7 @@ Expected: September 2022
|
||||||
### New features
|
### New features
|
||||||
|
|
||||||
* RESTCONF call home according to RFC 8071
|
* RESTCONF call home according to RFC 8071
|
||||||
|
* Experimental, work-in-progress
|
||||||
|
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
|
||||||
|
|
@ -102,7 +103,7 @@ Users may have to change how they access the system
|
||||||
Developers may need to change their code
|
Developers may need to change their code
|
||||||
|
|
||||||
* Changed C-API for xml translation/print the internal `cxobj` tree data structure to other formats.
|
* Changed C-API for xml translation/print the internal `cxobj` tree data structure to other formats.
|
||||||
* Functions are merged, ie removed and replaced with more generic functions
|
* Functions are merged, ie removed and with replaced more generic functions
|
||||||
* Added `skiptop` parameter, if set only apply to children of a node, skip top node
|
* Added `skiptop` parameter, if set only apply to children of a node, skip top node
|
||||||
* default is 0
|
* default is 0
|
||||||
* The new API is as follows:
|
* The new API is as follows:
|
||||||
|
|
|
||||||
|
|
@ -171,21 +171,6 @@
|
||||||
|
|
||||||
static int session_id_context = 1;
|
static int session_id_context = 1;
|
||||||
|
|
||||||
/*! Get restconf native handle
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @retval rh Restconf native handle
|
|
||||||
*/
|
|
||||||
static restconf_native_handle *
|
|
||||||
restconf_native_handle_get(clicon_handle h)
|
|
||||||
{
|
|
||||||
clicon_hash_t *cdat = clicon_data(h);
|
|
||||||
size_t len;
|
|
||||||
void *p;
|
|
||||||
|
|
||||||
if ((p = clicon_hash_value(cdat, "restconf-native-handle", &len)) != NULL)
|
|
||||||
return *(restconf_native_handle **)p;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Set restconf native handle
|
/*! Set restconf native handle
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
|
|
@ -489,11 +474,8 @@ Note that in this case SSL_ERROR_ZERO_RETURN does not necessarily indicate that
|
||||||
SSL_free(rc->rc_ssl);
|
SSL_free(rc->rc_ssl);
|
||||||
rc->rc_ssl = NULL;
|
rc->rc_ssl = NULL;
|
||||||
}
|
}
|
||||||
if (close(rc->rc_s) < 0){
|
if (restconf_connection_close(rc->rc_h, rc->rc_s) < 0)
|
||||||
clicon_err(OE_UNIX, errno, "close");
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
|
@ -643,9 +625,10 @@ ssl_alpn_check(clicon_handle h,
|
||||||
} /* ssl_alpn_check */
|
} /* ssl_alpn_check */
|
||||||
|
|
||||||
|
|
||||||
/*! Accept new socket client (ssl not ip)
|
/*! Accept new socket client. Note SSL not ip, this applies also to callhome
|
||||||
* @param[in] fd Socket (unix or ip)
|
* @param[in] h Clixon handle
|
||||||
* @param[in] arg typecast clicon_handle
|
* @param[in] s Socket (unix or ip)
|
||||||
|
* @param[in] rsock Socket struct
|
||||||
* @see openssl_init_socket where this callback is registered
|
* @see openssl_init_socket where this callback is registered
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -671,12 +654,10 @@ restconf_ssl_accept_client(clicon_handle h,
|
||||||
proto = HTTP_2; /* If nghttp2 only let default be 2.0 */
|
proto = HTTP_2; /* If nghttp2 only let default be 2.0 */
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#if 1
|
|
||||||
if ((rh = restconf_native_handle_get(h)) == NULL){
|
if ((rh = restconf_native_handle_get(h)) == NULL){
|
||||||
clicon_err(OE_XML, EFAULT, "No openssl handle");
|
clicon_err(OE_XML, EFAULT, "No openssl handle");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
/*
|
/*
|
||||||
* Register callbacks for actual data socket
|
* Register callbacks for actual data socket
|
||||||
*/
|
*/
|
||||||
|
|
@ -737,11 +718,8 @@ restconf_ssl_accept_client(clicon_handle h,
|
||||||
#endif
|
#endif
|
||||||
SSL_free(rc->rc_ssl);
|
SSL_free(rc->rc_ssl);
|
||||||
rc->rc_ssl = NULL;
|
rc->rc_ssl = NULL;
|
||||||
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
|
if (restconf_connection_close(h, rc->rc_s) < 0)
|
||||||
if (close(rc->rc_s) < 0){
|
|
||||||
clicon_err(OE_UNIX, errno, "close");
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
restconf_conn_free(rc);
|
restconf_conn_free(rc);
|
||||||
goto ok;
|
goto ok;
|
||||||
break;
|
break;
|
||||||
|
|
@ -961,8 +939,10 @@ restconf_native_terminate(clicon_handle h)
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
if ((rh = restconf_native_handle_get(h)) != NULL){
|
if ((rh = restconf_native_handle_get(h)) != NULL){
|
||||||
while ((rsock = rh->rh_sockets) != NULL){
|
while ((rsock = rh->rh_sockets) != NULL){
|
||||||
clixon_event_unreg_fd(rsock->rs_ss, restconf_accept_client);
|
if (rsock->rs_ss != -1){
|
||||||
close(rsock->rs_ss);
|
clixon_event_unreg_fd(rsock->rs_ss, restconf_accept_client);
|
||||||
|
close(rsock->rs_ss);
|
||||||
|
}
|
||||||
DELQ(rsock, rh->rh_sockets, restconf_socket *);
|
DELQ(rsock, rh->rh_sockets, restconf_socket *);
|
||||||
if (rsock->rs_addrstr)
|
if (rsock->rs_addrstr)
|
||||||
free(rsock->rs_addrstr);
|
free(rsock->rs_addrstr);
|
||||||
|
|
@ -1049,9 +1029,12 @@ restconf_clixon_backend(clicon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Periodically try to connect to callhome client
|
/*! Periodically try to connect to callhome client
|
||||||
|
*
|
||||||
|
* @param[in] fd No-op
|
||||||
|
* @param[in] arg restconf_socket
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
restconf_callhome_timer(int fd,
|
restconf_callhome_timer(int fdxxx,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -1059,7 +1042,7 @@ restconf_callhome_timer(int fd,
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
struct timeval t;
|
struct timeval t;
|
||||||
struct timeval t1 = {1, 0}; // XXX once every second
|
struct timeval t1 = {1, 0}; // XXX once every second
|
||||||
restconf_socket *rs;
|
restconf_socket *rsock = NULL;
|
||||||
struct sockaddr_in6 sin6 = {0,}; // because its larger than sin and sa
|
struct sockaddr_in6 sin6 = {0,}; // because its larger than sin and sa
|
||||||
struct sockaddr *sa = (struct sockaddr *)&sin6;
|
struct sockaddr *sa = (struct sockaddr *)&sin6;
|
||||||
size_t sa_len;
|
size_t sa_len;
|
||||||
|
|
@ -1067,12 +1050,13 @@ restconf_callhome_timer(int fd,
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
gettimeofday(&now, NULL);
|
gettimeofday(&now, NULL);
|
||||||
if ((rs = (restconf_socket *)arg) == NULL){
|
if ((rsock = (restconf_socket *)arg) == NULL){
|
||||||
clicon_err(OE_YANG, EINVAL, "rsock is NULL");
|
clicon_err(OE_YANG, EINVAL, "rsock is NULL");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
h = rs->rs_h;
|
h = rsock->rs_h;
|
||||||
if (clixon_inet2sin(rs->rs_addrtype, rs->rs_addrstr, rs->rs_port, sa, &sa_len) < 0)
|
/* Already computed in restconf_socket_init, could be saved in rsock? */
|
||||||
|
if (clixon_inet2sin(rsock->rs_addrtype, rsock->rs_addrstr, rsock->rs_port, sa, &sa_len) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((s = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
|
if ((s = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
|
||||||
clicon_err(OE_UNIX, errno, "socket");
|
clicon_err(OE_UNIX, errno, "socket");
|
||||||
|
|
@ -1080,18 +1064,18 @@ restconf_callhome_timer(int fd,
|
||||||
}
|
}
|
||||||
clicon_debug(1, "%s connect", __FUNCTION__);
|
clicon_debug(1, "%s connect", __FUNCTION__);
|
||||||
if (connect(s, sa, sa_len) < 0){
|
if (connect(s, sa, sa_len) < 0){
|
||||||
|
clicon_debug(1, "%s connect:%d %s", __FUNCTION__, errno, strerror(errno));
|
||||||
close(s);
|
close(s);
|
||||||
/* Fail: Initiate new timer */
|
/* Fail: Initiate new timer */
|
||||||
timeradd(&now, &t1, &t);
|
timeradd(&now, &t1, &t);
|
||||||
if (clixon_event_reg_timeout(t,
|
if (clixon_event_reg_timeout(t,
|
||||||
restconf_callhome_timer, /* this function */
|
restconf_callhome_timer, /* this function */
|
||||||
rs,
|
rsock,
|
||||||
"restconf callhome timer") < 0)
|
"restconf callhome timer") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rs->rs_ss = s;
|
if (restconf_ssl_accept_client(h, s, rsock) < 0)
|
||||||
if (restconf_ssl_accept_client(h, rs->rs_ss, rs) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
clicon_debug(1, "%s connect done", __FUNCTION__);
|
clicon_debug(1, "%s connect done", __FUNCTION__);
|
||||||
|
|
@ -1139,6 +1123,7 @@ openssl_init_socket(clicon_handle h,
|
||||||
memset(rsock, 0, sizeof *rsock);
|
memset(rsock, 0, sizeof *rsock);
|
||||||
rsock->rs_h = h;
|
rsock->rs_h = h;
|
||||||
rsock->rs_ssl = ssl; /* true/false */
|
rsock->rs_ssl = ssl; /* true/false */
|
||||||
|
rsock->rs_callhome = callhome;
|
||||||
if (callhome){
|
if (callhome){
|
||||||
if (!ssl){
|
if (!ssl){
|
||||||
clicon_err(OE_SSL, EINVAL, "Restconf callhome requires SSL");
|
clicon_err(OE_SSL, EINVAL, "Restconf callhome requires SSL");
|
||||||
|
|
@ -1162,9 +1147,6 @@ openssl_init_socket(clicon_handle h,
|
||||||
clicon_err(OE_XML, EFAULT, "No openssl handle");
|
clicon_err(OE_XML, EFAULT, "No openssl handle");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
rsock->rs_ss = ss;
|
|
||||||
|
|
||||||
if ((rsock->rs_addrstr = strdup(address)) == NULL){
|
if ((rsock->rs_addrstr = strdup(address)) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "strdup");
|
clicon_err(OE_UNIX, errno, "strdup");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1177,13 +1159,15 @@ openssl_init_socket(clicon_handle h,
|
||||||
INSQ(rsock, rh->rh_sockets);
|
INSQ(rsock, rh->rh_sockets);
|
||||||
|
|
||||||
if (callhome){
|
if (callhome){
|
||||||
if (restconf_callhome_timer(ss, rsock) < 0)
|
rsock->rs_ss = -1; /* Not applicable fro callhome */
|
||||||
|
if (restconf_callhome_timer(0, rsock) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* ss is a server socket that the clients connect to. The callback
|
/* ss is a server socket that the clients connect to. The callback
|
||||||
therefore accepts clients on ss */
|
therefore accepts clients on ss */
|
||||||
if (clixon_event_reg_fd(ss, restconf_accept_client, rsock, "restconf socket") < 0)
|
rsock->rs_ss = ss;
|
||||||
|
if (clixon_event_reg_fd(rsock->rs_ss, restconf_accept_client, rsock, "restconf socket") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,6 @@ restconf_stream_find(restconf_conn *rc,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @param[in] sd Restconf data stream
|
* @param[in] sd Restconf data stream
|
||||||
*/
|
*/
|
||||||
|
|
@ -338,10 +337,11 @@ restconf_connection_sanity(clicon_handle h,
|
||||||
* see also this function in restcont_api_openssl.c
|
* see also this function in restcont_api_openssl.c
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
native_buf_write(char *buf,
|
native_buf_write(clicon_handle h,
|
||||||
size_t buflen,
|
char *buf,
|
||||||
int s,
|
size_t buflen,
|
||||||
SSL *ssl)
|
int s,
|
||||||
|
SSL *ssl)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
@ -376,8 +376,8 @@ native_buf_write(char *buf,
|
||||||
if (ssl){
|
if (ssl){
|
||||||
SSL_free(ssl);
|
SSL_free(ssl);
|
||||||
}
|
}
|
||||||
close(s);
|
if (restconf_connection_close(h, s) < 0)
|
||||||
clixon_event_unreg_fd(s, restconf_connection);
|
goto done;
|
||||||
goto ok; /* Close socket and ssl */
|
goto ok; /* Close socket and ssl */
|
||||||
}
|
}
|
||||||
else if (er == EAGAIN){
|
else if (er == EAGAIN){
|
||||||
|
|
@ -408,8 +408,8 @@ native_buf_write(char *buf,
|
||||||
break;
|
break;
|
||||||
case ECONNRESET: /* Connection reset by peer */
|
case ECONNRESET: /* Connection reset by peer */
|
||||||
case EPIPE: /* Broken pipe */
|
case EPIPE: /* Broken pipe */
|
||||||
close(s);
|
if (restconf_connection_close(h, s) < 0)
|
||||||
clixon_event_unreg_fd(s, restconf_connection);
|
goto done;
|
||||||
goto ok; /* Close socket and ssl */
|
goto ok; /* Close socket and ssl */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -461,7 +461,7 @@ native_send_badrequest(clicon_handle h,
|
||||||
cprintf(cb, "\r\n");
|
cprintf(cb, "\r\n");
|
||||||
if (body)
|
if (body)
|
||||||
cprintf(cb, "%s\r\n", body);
|
cprintf(cb, "%s\r\n", body);
|
||||||
if (native_buf_write(cbuf_get(cb), cbuf_len(cb), s, ssl) < 0)
|
if (native_buf_write(h, cbuf_get(cb), cbuf_len(cb), s, ssl) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -574,8 +574,8 @@ read_regular(restconf_conn *rc,
|
||||||
switch(errno){
|
switch(errno){
|
||||||
case ECONNRESET:/* Connection reset by peer */
|
case ECONNRESET:/* Connection reset by peer */
|
||||||
clicon_debug(1, "%s %d Connection reset by peer", __FUNCTION__, rc->rc_s);
|
clicon_debug(1, "%s %d Connection reset by peer", __FUNCTION__, rc->rc_s);
|
||||||
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
|
if (restconf_connection_close(rc->rc_h, rc->rc_s) < 0)
|
||||||
close(rc->rc_s);
|
goto done;
|
||||||
restconf_conn_free(rc);
|
restconf_conn_free(rc);
|
||||||
retval = 0; /* Close socket and ssl */
|
retval = 0; /* Close socket and ssl */
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -685,7 +685,7 @@ restconf_http1_process(restconf_conn *rc,
|
||||||
if ((ret = http1_check_expect(h, rc, sd)) < 0)
|
if ((ret = http1_check_expect(h, rc, sd)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 1){
|
if (ret == 1){
|
||||||
if (native_buf_write(cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf),
|
if (native_buf_write(h, cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf),
|
||||||
rc->rc_s, rc->rc_ssl) < 0)
|
rc->rc_s, rc->rc_ssl) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
cvec_reset(sd->sd_outp_hdrs);
|
cvec_reset(sd->sd_outp_hdrs);
|
||||||
|
|
@ -713,7 +713,7 @@ restconf_http1_process(restconf_conn *rc,
|
||||||
/* main restconf processing */
|
/* main restconf processing */
|
||||||
if (restconf_http1_path_root(h, rc) < 0)
|
if (restconf_http1_path_root(h, rc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (native_buf_write(cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf),
|
if (native_buf_write(h, cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf),
|
||||||
rc->rc_s, rc->rc_ssl) < 0)
|
rc->rc_s, rc->rc_ssl) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
cvec_reset(sd->sd_outp_hdrs); /* Can be done in native_send_reply */
|
cvec_reset(sd->sd_outp_hdrs); /* Can be done in native_send_reply */
|
||||||
|
|
@ -723,11 +723,8 @@ restconf_http1_process(restconf_conn *rc,
|
||||||
if (rc->rc_exit){ /* Server-initiated exit */
|
if (rc->rc_exit){ /* Server-initiated exit */
|
||||||
SSL_free(rc->rc_ssl);
|
SSL_free(rc->rc_ssl);
|
||||||
rc->rc_ssl = NULL;
|
rc->rc_ssl = NULL;
|
||||||
if (close(rc->rc_s) < 0){
|
if (restconf_connection_close(h, rc->rc_s) < 0)
|
||||||
clicon_err(OE_UNIX, errno, "close");
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
|
|
||||||
restconf_conn_free(rc);
|
restconf_conn_free(rc);
|
||||||
retval = 0;
|
retval = 0;
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -856,6 +853,22 @@ restconf_http2_process(restconf_conn *rc,
|
||||||
}
|
}
|
||||||
#endif /* HAVE_LIBNGHTTP2 */
|
#endif /* HAVE_LIBNGHTTP2 */
|
||||||
|
|
||||||
|
/*! Get restconf native handle
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @retval rh Restconf native handle
|
||||||
|
*/
|
||||||
|
restconf_native_handle *
|
||||||
|
restconf_native_handle_get(clicon_handle h)
|
||||||
|
{
|
||||||
|
clicon_hash_t *cdat = clicon_data(h);
|
||||||
|
size_t len;
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
if ((p = clicon_hash_value(cdat, "restconf-native-handle", &len)) != NULL)
|
||||||
|
return *(restconf_native_handle **)p;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*! New data connection after accept, receive and reply on data socket
|
/*! New data connection after accept, receive and reply on data socket
|
||||||
*
|
*
|
||||||
* @param[in] s Socket where message arrived. read from this.
|
* @param[in] s Socket where message arrived. read from this.
|
||||||
|
|
@ -945,3 +958,38 @@ restconf_connection(int s,
|
||||||
clicon_debug(1, "%s retval %d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval %d", __FUNCTION__, retval);
|
||||||
return retval;
|
return retval;
|
||||||
} /* restconf_connection */
|
} /* restconf_connection */
|
||||||
|
|
||||||
|
int restconf_callhome_timer(int fd, void *arg); // XXX FIX HEADERS INSTEAD
|
||||||
|
|
||||||
|
/*! Close Restconf native connection socket and unregister callback
|
||||||
|
* For callhome also start reconnect timer
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
restconf_connection_close(clicon_handle h,
|
||||||
|
int s)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
restconf_native_handle *rh = NULL;
|
||||||
|
restconf_socket *rsock;
|
||||||
|
|
||||||
|
if (close(s) < 0){
|
||||||
|
clicon_err(OE_UNIX, errno, "close");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
clixon_event_unreg_fd(s, restconf_connection);
|
||||||
|
|
||||||
|
if (0 && (rh = restconf_native_handle_get(h)) != NULL){
|
||||||
|
while ((rsock = rh->rh_sockets) != NULL)
|
||||||
|
if (rsock->rs_callhome && s == rsock->rs_ss){
|
||||||
|
/* Maybe we should have a special wrapper function that only sets timer here? */
|
||||||
|
if (restconf_callhome_timer(0, rsock) < 0)
|
||||||
|
goto done;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,9 +92,9 @@ typedef struct restconf_conn {
|
||||||
/* XXX rc_proto and rc_proto_d1/d2 may not both be necessary.
|
/* XXX rc_proto and rc_proto_d1/d2 may not both be necessary.
|
||||||
* remove rc_proto?
|
* remove rc_proto?
|
||||||
*/
|
*/
|
||||||
restconf_http_proto rc_proto; /* HTTP protocol: http/1 or http/2 */
|
restconf_http_proto rc_proto; /* HTTP protocol: http/1 or http/2 */
|
||||||
int rc_proto_d1; /* parsed version digit 1 */
|
int rc_proto_d1; /* parsed version digit 1 */
|
||||||
int rc_proto_d2; /* parsed version digit 2 */
|
int rc_proto_d2; /* parsed version digit 2 */
|
||||||
int rc_s; /* Connection socket */
|
int rc_s; /* Connection socket */
|
||||||
clicon_handle rc_h; /* Clixon handle */
|
clicon_handle rc_h; /* Clixon handle */
|
||||||
SSL *rc_ssl; /* Structure for SSL connection */
|
SSL *rc_ssl; /* Structure for SSL connection */
|
||||||
|
|
@ -108,11 +108,18 @@ typedef struct restconf_conn {
|
||||||
} restconf_conn;
|
} restconf_conn;
|
||||||
|
|
||||||
/* Restconf per socket handle
|
/* Restconf per socket handle
|
||||||
|
* Two types: listen and callhome.
|
||||||
|
* Listen: Uses socket rs_ss to listen for connections and accepts them, creates one
|
||||||
|
* restconf_conn for each new accept.
|
||||||
|
* Callhome: Calls connect according to timer to setup single restconf_conn.
|
||||||
|
* when this is closed, new connect is made, according to connection-type.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
qelem_t rs_qelem; /* List header */
|
qelem_t rs_qelem; /* List header */
|
||||||
clicon_handle rs_h; /* Clixon handle */
|
clicon_handle rs_h; /* Clixon handle */
|
||||||
int rs_ss; /* Server socket (ready for accept) */
|
int rs_callhome; /* 0: listen, 1: callhome */
|
||||||
|
int rs_ss; /* Listen: Server socket, ready for accept
|
||||||
|
* Callhome: connect socket (same as restconf_conn->rc_s) */
|
||||||
int rs_ssl; /* 0: Not SSL socket, 1:SSL socket */
|
int rs_ssl; /* 0: Not SSL socket, 1:SSL socket */
|
||||||
char *rs_addrtype; /* Address type according to ietf-inet-types:
|
char *rs_addrtype; /* Address type according to ietf-inet-types:
|
||||||
eg inet:ipv4-address or inet:ipv6-address */
|
eg inet:ipv4-address or inet:ipv6-address */
|
||||||
|
|
@ -142,7 +149,9 @@ int ssl_x509_name_oneline(SSL *ssl, char **oneline);
|
||||||
int restconf_close_ssl_socket(restconf_conn *rc, int shutdown); /* XXX in restconf_main_native.c */
|
int restconf_close_ssl_socket(restconf_conn *rc, int shutdown); /* XXX in restconf_main_native.c */
|
||||||
int restconf_connection_sanity(clicon_handle h, restconf_conn *rc, restconf_stream_data *sd);
|
int restconf_connection_sanity(clicon_handle h, restconf_conn *rc, restconf_stream_data *sd);
|
||||||
int native_send_badrequest(clicon_handle h, int s, SSL *ssl, char *media, char *body);
|
int native_send_badrequest(clicon_handle h, int s, SSL *ssl, char *media, char *body);
|
||||||
|
restconf_native_handle *restconf_native_handle_get(clicon_handle h);
|
||||||
int restconf_connection(int s, void *arg);
|
int restconf_connection(int s, void *arg);
|
||||||
|
int restconf_connection_close(clicon_handle h, int s);
|
||||||
|
|
||||||
#endif /* _RESTCONF_NATIVE_H_ */
|
#endif /* _RESTCONF_NATIVE_H_ */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,10 @@ session_send_callback(nghttp2_session *session,
|
||||||
#if 1
|
#if 1
|
||||||
else if (errno == ECONNRESET) {/* Connection reset by peer */
|
else if (errno == ECONNRESET) {/* Connection reset by peer */
|
||||||
close(s);
|
close(s);
|
||||||
// XXX clixon_event_unreg_fd(s, restconf_connection);
|
// XXXUnclear why this is commented, maybe should call
|
||||||
|
// restconf_connection_close?
|
||||||
|
// clixon_event_unreg_fd(s, restconf_connection);
|
||||||
|
|
||||||
goto ok; /* Close socket and ssl */
|
goto ok; /* Close socket and ssl */
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -190,9 +190,6 @@ BUSER=clicon
|
||||||
|
|
||||||
: ${clixon_snmp_pidfile:="/var/tmp/clixon_snmp.pid"}
|
: ${clixon_snmp_pidfile:="/var/tmp/clixon_snmp.pid"}
|
||||||
|
|
||||||
# Temporary debug var, set to trigger remaining snmp errors
|
|
||||||
: ${snmp_debug:=false}
|
|
||||||
|
|
||||||
# Source the site-specific definitions for test script variables, if site.sh
|
# Source the site-specific definitions for test script variables, if site.sh
|
||||||
# exists. The variables defined in site.sh override any variables of the same
|
# exists. The variables defined in site.sh override any variables of the same
|
||||||
# names in the environment in the current execution.
|
# names in the environment in the current execution.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# test of Restconf callhome
|
# test of Restconf callhome
|
||||||
# See RFC 8071 NETCONF Call Home and RESTCONF Call Home
|
# See RFC 8071 NETCONF Call Home and RESTCONF Call Home
|
||||||
# No NACM for now
|
# Simple NACM for single "andy" user
|
||||||
|
|
||||||
# Magic line must be first in script (see README.md)
|
# Magic line must be first in script (see README.md)
|
||||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
@ -49,6 +49,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_YANG_LIBRARY>false</CLICON_YANG_LIBRARY>
|
<CLICON_YANG_LIBRARY>false</CLICON_YANG_LIBRARY>
|
||||||
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
|
<CLICON_SOCK>$dir/$APPNAME.sock</CLICON_SOCK>
|
||||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||||
<restconf>
|
<restconf>
|
||||||
<enable>true</enable>
|
<enable>true</enable>
|
||||||
|
|
@ -109,6 +110,9 @@ module clixon-example{
|
||||||
yang-version 1.1;
|
yang-version 1.1;
|
||||||
namespace "urn:example:clixon";
|
namespace "urn:example:clixon";
|
||||||
prefix ex;
|
prefix ex;
|
||||||
|
import ietf-netconf-acm {
|
||||||
|
prefix nacm;
|
||||||
|
}
|
||||||
/* Generic config data */
|
/* Generic config data */
|
||||||
container table{
|
container table{
|
||||||
list parameter{
|
list parameter{
|
||||||
|
|
@ -124,6 +128,37 @@ module clixon-example{
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# NACM rules
|
||||||
|
# Two groups: admin allow all, guest allow nothing
|
||||||
|
RULES=$(cat <<EOF
|
||||||
|
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
|
||||||
|
<enable-nacm>false</enable-nacm>
|
||||||
|
<read-default>permit</read-default>
|
||||||
|
<write-default>deny</write-default>
|
||||||
|
<exec-default>deny</exec-default>
|
||||||
|
<groups>
|
||||||
|
<group>
|
||||||
|
<name>admin</name>
|
||||||
|
<user-name>andy</user-name>
|
||||||
|
</group>
|
||||||
|
</groups>
|
||||||
|
<rule-list>
|
||||||
|
<name>admin-acl</name>
|
||||||
|
<group>admin</group>
|
||||||
|
<rule>
|
||||||
|
<name>permit-all</name>
|
||||||
|
<module-name>*</module-name>
|
||||||
|
<access-operations>*</access-operations>
|
||||||
|
<action>permit</action>
|
||||||
|
<comment>
|
||||||
|
Allow the 'admin' group complete access to all operations and data.
|
||||||
|
</comment>
|
||||||
|
</rule>
|
||||||
|
</rule-list>
|
||||||
|
</nacm>
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
cat <<EOF > $clispec
|
cat <<EOF > $clispec
|
||||||
CLICON_MODE="example";
|
CLICON_MODE="example";
|
||||||
CLICON_PROMPT="%U@%H %W> ";
|
CLICON_PROMPT="%U@%H %W> ";
|
||||||
|
|
@ -177,15 +212,16 @@ EOF
|
||||||
openssl x509 -req -extfile $dir/$name.cnf -days 7 -passin "pass:password" -in $certdir/$name.csr -CA $cacert -CAkey $cakey -CAcreateserial -out $certdir/$name.crt || err "Generate signing client cert"
|
openssl x509 -req -extfile $dir/$name.cnf -days 7 -passin "pass:password" -in $certdir/$name.csr -CA $cacert -CAkey $cakey -CAcreateserial -out $certdir/$name.crt || err "Generate signing client cert"
|
||||||
done
|
done
|
||||||
|
|
||||||
# hardcoded to andy
|
# Just NACM for now
|
||||||
cat <<EOF > $dir/startup_db
|
cat <<EOF > $dir/startup_db
|
||||||
<${DATASTORE_TOP}>
|
<${DATASTORE_TOP}>
|
||||||
|
$RULES
|
||||||
</${DATASTORE_TOP}>
|
</${DATASTORE_TOP}>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Callhome request from client
|
# Callhome request from client
|
||||||
cat <<EOF > $dir/data
|
cat <<EOF > $dir/data
|
||||||
GET /restconf/data HTTP/$HVERCH
|
GET /restconf/data/clixon-example:table HTTP/$HVERCH
|
||||||
Host: localhost
|
Host: localhost
|
||||||
Accept: application/yang-data+xml
|
Accept: application/yang-data+xml
|
||||||
|
|
||||||
|
|
@ -223,6 +259,7 @@ new "restconf Add init data"
|
||||||
expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X POST -H "Accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" -d '{"clixon-example:table":{"parameter":{"name":"x","value":"foo"}}}' $RCPROTO://127.0.0.1/restconf/data)" 0 "HTTP/$HVER 201"
|
expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X POST -H "Accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" -d '{"clixon-example:table":{"parameter":{"name":"x","value":"foo"}}}' $RCPROTO://127.0.0.1/restconf/data)" 0 "HTTP/$HVER 201"
|
||||||
|
|
||||||
new "Send GET via callhome client"
|
new "Send GET via callhome client"
|
||||||
|
echo "${clixon_restconf_callhome_client} -D $DBG -f $dir/data -a 127.0.0.1 -c $srvcert -k $srvkey -C $cacert"
|
||||||
expectpart "$(${clixon_restconf_callhome_client} -D $DBG -f $dir/data -a 127.0.0.1 -c $srvcert -k $srvkey -C $cacert)" 0 "HTTP/$HVERCH 200 OK" "Content-Type: application/yang-data+xml"
|
expectpart "$(${clixon_restconf_callhome_client} -D $DBG -f $dir/data -a 127.0.0.1 -c $srvcert -k $srvkey -C $cacert)" 0 "HTTP/$HVERCH 200 OK" "Content-Type: application/yang-data+xml"
|
||||||
|
|
||||||
# Kill old
|
# Kill old
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,9 @@
|
||||||
| clixon_restconf | ----------------> | callhome-client | <------ 3) HTTP
|
| clixon_restconf | ----------------> | callhome-client | <------ 3) HTTP
|
||||||
| | 2) tls | |
|
| | 2) tls | |
|
||||||
+-----------------+ <--------------- +-----------------+
|
+-----------------+ <--------------- +-----------------+
|
||||||
|
|
||||||
|
The callhome-client listens on accept, when connect comes in, creates data socket and sends
|
||||||
|
RESTCONF GET to server, then re-waits for new accepts.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
|
|
@ -64,15 +66,15 @@
|
||||||
/* clixon */
|
/* clixon */
|
||||||
#include "clixon/clixon.h"
|
#include "clixon/clixon.h"
|
||||||
|
|
||||||
#define UTIL_TLS_OPTS "hD:f:F:a:p:c:C:k:"
|
#define UTIL_TLS_OPTS "hD:f:F:a:p:c:C:k:n:"
|
||||||
|
|
||||||
#define RESTCONF_CH_TLS 4336
|
#define RESTCONF_CH_TLS 4336
|
||||||
|
|
||||||
/* User struct for context / accept */
|
/* User struct for context / accept */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int ta_ss; /* accept socket */
|
int ta_ss; /* Accept socket */
|
||||||
SSL_CTX *ta_ctx; /* SSL context */
|
SSL_CTX *ta_ctx; /* SSL context */
|
||||||
FILE *ta_f; /* Input data file */
|
FILE *ta_f; /* Input data file */
|
||||||
} tls_accept_handle;
|
} tls_accept_handle;
|
||||||
|
|
||||||
/* User connection-specific data handle */
|
/* User connection-specific data handle */
|
||||||
|
|
@ -81,6 +83,9 @@ typedef struct {
|
||||||
SSL *sd_ssl; /* SSL connection data */
|
SSL *sd_ssl; /* SSL connection data */
|
||||||
} tls_session_data;
|
} tls_session_data;
|
||||||
|
|
||||||
|
/* Expected connects */
|
||||||
|
static int _connects = 1;
|
||||||
|
|
||||||
/*! Create and bind stream socket
|
/*! Create and bind stream socket
|
||||||
* @param[in] sa Socketaddress
|
* @param[in] sa Socketaddress
|
||||||
* @param[in] sa_len Length of sa. Tecynicaliyu to be independent of sockaddr sa_len
|
* @param[in] sa_len Length of sa. Tecynicaliyu to be independent of sockaddr sa_len
|
||||||
|
|
@ -168,8 +173,13 @@ tls_input_cb(int s,
|
||||||
clixon_event_unreg_fd(s, tls_input_cb);
|
clixon_event_unreg_fd(s, tls_input_cb);
|
||||||
close(s);
|
close(s);
|
||||||
free(sd);
|
free(sd);
|
||||||
clixon_exit_set(1); /* XXX more elaborate logic: 1) continue request, 2) close and accept new */
|
if (_connects == 1)
|
||||||
|
clixon_exit_set(1); /* XXX more elaborate logic: 1) continue request, 2) close and accept new */
|
||||||
|
else
|
||||||
|
_connects--;
|
||||||
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,8 +393,6 @@ tls_ctx_init(const char *cert_path,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
usage(char *argv0)
|
usage(char *argv0)
|
||||||
{
|
{
|
||||||
|
|
@ -399,6 +407,7 @@ usage(char *argv0)
|
||||||
"\t-c <path> \tcert\n"
|
"\t-c <path> \tcert\n"
|
||||||
"\t-C <path> \tcacert\n"
|
"\t-C <path> \tcacert\n"
|
||||||
"\t-k <path> \tkey\n"
|
"\t-k <path> \tkey\n"
|
||||||
|
"\t-n <nr> \tExpected incoming connections, 0 means no limit. Default: 1\n"
|
||||||
,
|
,
|
||||||
argv0,
|
argv0,
|
||||||
RESTCONF_CH_TLS);
|
RESTCONF_CH_TLS);
|
||||||
|
|
@ -472,6 +481,11 @@ main(int argc,
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
key_path = optarg;
|
key_path = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'n':
|
||||||
|
if (optarg == NULL || *optarg == '-')
|
||||||
|
usage(argv[0]);
|
||||||
|
_connects = atoi(optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,7 @@ module clixon-restconf {
|
||||||
"Selects between available connection types.";
|
"Selects between available connection types.";
|
||||||
case persistent-connection {
|
case persistent-connection {
|
||||||
container persistent {
|
container persistent {
|
||||||
presence
|
presence
|
||||||
"Indicates that a persistent connection is to be
|
"Indicates that a persistent connection is to be
|
||||||
maintained.";
|
maintained.";
|
||||||
}
|
}
|
||||||
|
|
@ -298,6 +298,25 @@ module clixon-restconf {
|
||||||
container periodic {
|
container periodic {
|
||||||
presence
|
presence
|
||||||
"Indicates periodic connects";
|
"Indicates periodic connects";
|
||||||
|
leaf period {
|
||||||
|
type uint16;
|
||||||
|
units "minutes";
|
||||||
|
default "60";
|
||||||
|
description
|
||||||
|
"Duration of time between periodic connections.";
|
||||||
|
}
|
||||||
|
leaf idle-timeout {
|
||||||
|
type uint16;
|
||||||
|
units "seconds";
|
||||||
|
default "120"; // two minutes
|
||||||
|
description
|
||||||
|
"Specifies the maximum number of seconds that
|
||||||
|
the underlying TCP session may remain idle.
|
||||||
|
A TCP session will be dropped if it is idle
|
||||||
|
for an interval longer than this number of
|
||||||
|
seconds. If set to zero, then the server
|
||||||
|
will never drop a session because it is idle.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue