Restconf callhome: Added YANG connection-type, test: added NACM

This commit is contained in:
Olof hagsand 2022-08-10 13:40:03 +02:00
parent 9de052c144
commit 7e783952eb
9 changed files with 194 additions and 82 deletions

View file

@ -171,21 +171,6 @@
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
* @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);
rc->rc_ssl = NULL;
}
if (close(rc->rc_s) < 0){
clicon_err(OE_UNIX, errno, "close");
if (restconf_connection_close(rc->rc_h, rc->rc_s) < 0)
goto done;
}
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
@ -643,9 +625,10 @@ ssl_alpn_check(clicon_handle h,
} /* ssl_alpn_check */
/*! Accept new socket client (ssl not ip)
* @param[in] fd Socket (unix or ip)
* @param[in] arg typecast clicon_handle
/*! Accept new socket client. Note SSL not ip, this applies also to callhome
* @param[in] h Clixon handle
* @param[in] s Socket (unix or ip)
* @param[in] rsock Socket struct
* @see openssl_init_socket where this callback is registered
*/
static int
@ -671,12 +654,10 @@ restconf_ssl_accept_client(clicon_handle h,
proto = HTTP_2; /* If nghttp2 only let default be 2.0 */
#endif
#endif
#if 1
if ((rh = restconf_native_handle_get(h)) == NULL){
clicon_err(OE_XML, EFAULT, "No openssl handle");
goto done;
}
#endif
/*
* Register callbacks for actual data socket
*/
@ -737,11 +718,8 @@ restconf_ssl_accept_client(clicon_handle h,
#endif
SSL_free(rc->rc_ssl);
rc->rc_ssl = NULL;
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
if (close(rc->rc_s) < 0){
clicon_err(OE_UNIX, errno, "close");
if (restconf_connection_close(h, rc->rc_s) < 0)
goto done;
}
restconf_conn_free(rc);
goto ok;
break;
@ -961,8 +939,10 @@ restconf_native_terminate(clicon_handle h)
clicon_debug(1, "%s", __FUNCTION__);
if ((rh = restconf_native_handle_get(h)) != NULL){
while ((rsock = rh->rh_sockets) != NULL){
clixon_event_unreg_fd(rsock->rs_ss, restconf_accept_client);
close(rsock->rs_ss);
if (rsock->rs_ss != -1){
clixon_event_unreg_fd(rsock->rs_ss, restconf_accept_client);
close(rsock->rs_ss);
}
DELQ(rsock, rh->rh_sockets, restconf_socket *);
if (rsock->rs_addrstr)
free(rsock->rs_addrstr);
@ -1049,9 +1029,12 @@ restconf_clixon_backend(clicon_handle h,
}
/*! Periodically try to connect to callhome client
*
* @param[in] fd No-op
* @param[in] arg restconf_socket
*/
int
restconf_callhome_timer(int fd,
restconf_callhome_timer(int fdxxx,
void *arg)
{
int retval = -1;
@ -1059,7 +1042,7 @@ restconf_callhome_timer(int fd,
struct timeval now;
struct timeval t;
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 *sa = (struct sockaddr *)&sin6;
size_t sa_len;
@ -1067,12 +1050,13 @@ restconf_callhome_timer(int fd,
clicon_debug(1, "%s", __FUNCTION__);
gettimeofday(&now, NULL);
if ((rs = (restconf_socket *)arg) == NULL){
if ((rsock = (restconf_socket *)arg) == NULL){
clicon_err(OE_YANG, EINVAL, "rsock is NULL");
goto done;
}
h = rs->rs_h;
if (clixon_inet2sin(rs->rs_addrtype, rs->rs_addrstr, rs->rs_port, sa, &sa_len) < 0)
h = rsock->rs_h;
/* 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;
if ((s = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
clicon_err(OE_UNIX, errno, "socket");
@ -1080,18 +1064,18 @@ restconf_callhome_timer(int fd,
}
clicon_debug(1, "%s connect", __FUNCTION__);
if (connect(s, sa, sa_len) < 0){
clicon_debug(1, "%s connect:%d %s", __FUNCTION__, errno, strerror(errno));
close(s);
/* Fail: Initiate new timer */
timeradd(&now, &t1, &t);
if (clixon_event_reg_timeout(t,
restconf_callhome_timer, /* this function */
rs,
rsock,
"restconf callhome timer") < 0)
goto done;
}
else {
rs->rs_ss = s;
if (restconf_ssl_accept_client(h, rs->rs_ss, rs) < 0)
if (restconf_ssl_accept_client(h, s, rsock) < 0)
goto done;
}
clicon_debug(1, "%s connect done", __FUNCTION__);
@ -1139,6 +1123,7 @@ openssl_init_socket(clicon_handle h,
memset(rsock, 0, sizeof *rsock);
rsock->rs_h = h;
rsock->rs_ssl = ssl; /* true/false */
rsock->rs_callhome = callhome;
if (callhome){
if (!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");
goto done;
}
rsock->rs_ss = ss;
if ((rsock->rs_addrstr = strdup(address)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
@ -1177,13 +1159,15 @@ openssl_init_socket(clicon_handle h,
INSQ(rsock, rh->rh_sockets);
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;
}
else {
/* ss is a server socket that the clients connect to. The callback
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;
}
retval = 0;

View file

@ -135,7 +135,6 @@ restconf_stream_find(restconf_conn *rc,
return NULL;
}
/*
* @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
*/
static int
native_buf_write(char *buf,
size_t buflen,
int s,
SSL *ssl)
native_buf_write(clicon_handle h,
char *buf,
size_t buflen,
int s,
SSL *ssl)
{
int retval = -1;
ssize_t len;
@ -376,8 +376,8 @@ native_buf_write(char *buf,
if (ssl){
SSL_free(ssl);
}
close(s);
clixon_event_unreg_fd(s, restconf_connection);
if (restconf_connection_close(h, s) < 0)
goto done;
goto ok; /* Close socket and ssl */
}
else if (er == EAGAIN){
@ -408,8 +408,8 @@ native_buf_write(char *buf,
break;
case ECONNRESET: /* Connection reset by peer */
case EPIPE: /* Broken pipe */
close(s);
clixon_event_unreg_fd(s, restconf_connection);
if (restconf_connection_close(h, s) < 0)
goto done;
goto ok; /* Close socket and ssl */
break;
default:
@ -461,7 +461,7 @@ native_send_badrequest(clicon_handle h,
cprintf(cb, "\r\n");
if (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;
retval = 0;
done:
@ -574,8 +574,8 @@ read_regular(restconf_conn *rc,
switch(errno){
case ECONNRESET:/* Connection reset by peer */
clicon_debug(1, "%s %d Connection reset by peer", __FUNCTION__, rc->rc_s);
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
close(rc->rc_s);
if (restconf_connection_close(rc->rc_h, rc->rc_s) < 0)
goto done;
restconf_conn_free(rc);
retval = 0; /* Close socket and ssl */
goto done;
@ -685,7 +685,7 @@ restconf_http1_process(restconf_conn *rc,
if ((ret = http1_check_expect(h, rc, sd)) < 0)
goto done;
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)
goto done;
cvec_reset(sd->sd_outp_hdrs);
@ -713,7 +713,7 @@ restconf_http1_process(restconf_conn *rc,
/* main restconf processing */
if (restconf_http1_path_root(h, rc) < 0)
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)
goto done;
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 */
SSL_free(rc->rc_ssl);
rc->rc_ssl = NULL;
if (close(rc->rc_s) < 0){
clicon_err(OE_UNIX, errno, "close");
if (restconf_connection_close(h, rc->rc_s) < 0)
goto done;
}
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
restconf_conn_free(rc);
retval = 0;
goto done;
@ -856,6 +853,22 @@ restconf_http2_process(restconf_conn *rc,
}
#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
*
* @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);
return retval;
} /* 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;
}

View file

@ -92,9 +92,9 @@ typedef struct restconf_conn {
/* XXX rc_proto and rc_proto_d1/d2 may not both be necessary.
* remove rc_proto?
*/
restconf_http_proto rc_proto; /* HTTP protocol: http/1 or http/2 */
int rc_proto_d1; /* parsed version digit 1 */
int rc_proto_d2; /* parsed version digit 2 */
restconf_http_proto rc_proto; /* HTTP protocol: http/1 or http/2 */
int rc_proto_d1; /* parsed version digit 1 */
int rc_proto_d2; /* parsed version digit 2 */
int rc_s; /* Connection socket */
clicon_handle rc_h; /* Clixon handle */
SSL *rc_ssl; /* Structure for SSL connection */
@ -108,11 +108,18 @@ typedef struct restconf_conn {
} restconf_conn;
/* 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 {
qelem_t rs_qelem; /* List header */
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 */
char *rs_addrtype; /* Address type according to ietf-inet-types:
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_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);
restconf_native_handle *restconf_native_handle_get(clicon_handle h);
int restconf_connection(int s, void *arg);
int restconf_connection_close(clicon_handle h, int s);
#endif /* _RESTCONF_NATIVE_H_ */

View file

@ -224,7 +224,10 @@ session_send_callback(nghttp2_session *session,
#if 1
else if (errno == ECONNRESET) {/* Connection reset by peer */
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 */
}
#endif