Added CLIXON_CLIENT_SSH to client API to communicate remotely via SSH netconf sub-system
configure: stringified SSH_BIN C-API: Added `sock_flags` parameter to `clixon_proc_socket()`
This commit is contained in:
parent
100f15b699
commit
6baa904039
10 changed files with 171 additions and 95 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -41,6 +41,16 @@
|
|||
## 6.1.0
|
||||
Expected: beginning of 2023
|
||||
|
||||
### C/CLI-API changes on existing features
|
||||
Developers may need to change their code
|
||||
|
||||
* C-API
|
||||
* Added `sock_flags` parameter to `clixon_proc_socket()`
|
||||
|
||||
### Minor features
|
||||
|
||||
* Added `CLIXON_CLIENT_SSH` to client API to communicate remotely via SSH netconf sub-system
|
||||
|
||||
### Corrected Bugs
|
||||
|
||||
* Fixed [Netconf monitoring](https://github.com/clicon/clixon/issues/370)
|
||||
|
|
|
|||
2
configure
vendored
2
configure
vendored
|
|
@ -4607,7 +4607,7 @@ fi
|
|||
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define SSH_BIN $SSH_BIN
|
||||
#define SSH_BIN "$SSH_BIN"
|
||||
_ACEOF
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ fi
|
|||
|
||||
# SSH binary path
|
||||
AC_PATH_PROG(SSH_BIN, ssh)
|
||||
AC_DEFINE_UNQUOTED(SSH_BIN, $SSH_BIN, [SSH binary])
|
||||
AC_DEFINE_UNQUOTED(SSH_BIN, "$SSH_BIN", [SSH binary])
|
||||
|
||||
# Get "bison" from bison -y or other string
|
||||
if test "$YACC" = "${YACC##bison}" ; then
|
||||
|
|
|
|||
|
|
@ -42,11 +42,25 @@
|
|||
typedef void *clixon_handle;
|
||||
typedef void *clixon_client_handle;
|
||||
|
||||
/* Connection type as parameter to connect */
|
||||
/* Connection type as parameter to connect
|
||||
*/
|
||||
typedef enum {
|
||||
CLIXON_CLIENT_IPC, /* Internal IPC API, only experimental use */
|
||||
CLIXON_CLIENT_NETCONF, /* External Netconf */
|
||||
CLIXON_CLIENT_SSH /* NYI External Netconf over SSH */
|
||||
/* Internal IPC API, connect directly on local UNIX domain socket to backend
|
||||
* or using IP according to CLICON_SOCK_FAMILY setting
|
||||
* see https://clixon-docs.readthedocs.io/en/latest/netconf.html#ipc
|
||||
* Must be local on device
|
||||
*/
|
||||
CLIXON_CLIENT_IPC,
|
||||
/* Regular NETCONF via local netconf binary
|
||||
* Fork clixon_netconf locally which in turn communicates with backend
|
||||
* Must be local on device
|
||||
*/
|
||||
CLIXON_CLIENT_NETCONF,
|
||||
/* Regular NETCONF using ssh sub-system via local SSH (openssh) client binary
|
||||
* Fork ssh locally which in turn communicates remotely to device
|
||||
* Must have openssh installed locally and device must have ssh sub-subsystem
|
||||
*/
|
||||
CLIXON_CLIENT_SSH
|
||||
} clixon_client_type;
|
||||
|
||||
/*
|
||||
|
|
@ -59,7 +73,9 @@ extern "C" {
|
|||
|
||||
clixon_handle clixon_client_init(const char *config_file);
|
||||
int clixon_client_terminate(clixon_handle h);
|
||||
clixon_client_handle clixon_client_connect(clixon_handle h, clixon_client_type socktype);
|
||||
int clixon_client_lock(int sock, const int lock, const char *db);
|
||||
int clixon_client_hello(int sock, int version);
|
||||
clixon_client_handle clixon_client_connect(clixon_handle h, clixon_client_type socktype, const char *dest);
|
||||
int clixon_client_disconnect(clixon_client_handle ch);
|
||||
int clixon_client_get_bool(clixon_client_handle ch, int *rval, const char *xnamespace, const char *xpath);
|
||||
int clixon_client_get_str(clixon_client_handle ch, char *rval, int n, const char *xnamespace, const char *xpath);
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ enum error_option{ /* edit-config */
|
|||
/* NETCONF framing
|
||||
*/
|
||||
enum framing_type{
|
||||
NETCONF_SSH_EOM, /* RFC 4742, RFC 6242 hello msg (end-of-msg: ]]>]]>)*/
|
||||
NETCONF_SSH_EOM=0, /* RFC 4742, RFC 6242 hello msg (end-of-msg: ]]>]]>)*/
|
||||
NETCONF_SSH_CHUNKED, /* RFC 6242 Chunked framing */
|
||||
};
|
||||
typedef enum framing_type netconf_framing_type;
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ typedef int (proc_cb_t)(clicon_handle h, process_entry_t *pe, proc_operation *op
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int clixon_proc_socket(char **argv, pid_t *pid, int *sock);
|
||||
int clixon_proc_socket(char **argv, int sock_flags, pid_t *pid, int *sock);
|
||||
int clixon_proc_socket_close(pid_t pid, int sock);
|
||||
int clixon_proc_background(char **argv, const char *netns, pid_t *pid);
|
||||
int clixon_process_pid(clicon_handle h, const char *name, pid_t *pid);
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ clixon_client_terminate(clicon_handle h)
|
|||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
int
|
||||
clixon_client_lock(int sock,
|
||||
const int lock,
|
||||
const char *db)
|
||||
|
|
@ -204,15 +204,14 @@ clixon_client_lock(int sock,
|
|||
|
||||
/*! Internal function to construct the encoding and hello message
|
||||
*
|
||||
* @param[in] sock Socket
|
||||
* @param[in] namespace Default namespace used for non-prefixed entries in xpath. (Alt use nsc)
|
||||
* @param[in] xpath XPath
|
||||
* @param[out] xdata XML data tree (may or may not include the intended data)
|
||||
* @param[in] sock Socket to netconf server
|
||||
* @param[in] version Netconf version for capability announcement
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
clixon_client_hello(int sock)
|
||||
int
|
||||
clixon_client_hello(int sock,
|
||||
int version)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *msg = NULL;
|
||||
|
|
@ -222,9 +221,11 @@ clixon_client_hello(int sock)
|
|||
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(msg, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
// cprintf(msg, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
cprintf(msg, "<hello xmlns=\"%s\">", NETCONF_BASE_NAMESPACE);
|
||||
cprintf(msg, "<capabilities><capability>%s</capability></capabilities>", NETCONF_BASE_CAPABILITY_1_1);
|
||||
cprintf(msg, "<capabilities>");
|
||||
cprintf(msg, "<capability>%s</capability>", version==0?NETCONF_BASE_CAPABILITY_1_0:NETCONF_BASE_CAPABILITY_1_1);
|
||||
cprintf(msg, "</capabilities>");
|
||||
cprintf(msg, "</hello>");
|
||||
cprintf(msg, "]]>]]>");
|
||||
if (clicon_msg_send1(sock, msg) < 0)
|
||||
|
|
@ -237,25 +238,112 @@ clixon_client_hello(int sock)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
clixon_client_connect_netconf(clicon_handle h,
|
||||
struct clixon_client_handle *cch)
|
||||
{
|
||||
int retval = -1;
|
||||
int nr;
|
||||
int i;
|
||||
char **argv = NULL;
|
||||
char *netconf_bin = NULL;
|
||||
struct stat st = {0,};
|
||||
char dbgstr[8];
|
||||
|
||||
nr = 7;
|
||||
if (clicon_debug_get() != 0)
|
||||
nr += 2;
|
||||
if ((argv = calloc(nr, sizeof(char *))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto done;
|
||||
}
|
||||
i = 0;
|
||||
if ((netconf_bin = getenv("CLIXON_NETCONF_BIN")) == NULL)
|
||||
netconf_bin = CLIXON_NETCONF_BIN;
|
||||
if (stat(netconf_bin, &st) < 0){
|
||||
clicon_err(OE_NETCONF, errno, "netconf binary %s. Set with CLIXON_NETCONF_BIN=",
|
||||
netconf_bin);
|
||||
goto done;
|
||||
}
|
||||
argv[i++] = netconf_bin;
|
||||
argv[i++] = "-q";
|
||||
argv[i++] = "-f";
|
||||
argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE");
|
||||
argv[i++] = "-l"; /* log to syslog */
|
||||
argv[i++] = "s";
|
||||
if (clicon_debug_get() != 0){
|
||||
argv[i++] = "-D";
|
||||
snprintf(dbgstr, sizeof(dbgstr)-1, "%d", clicon_debug_get());
|
||||
argv[i++] = dbgstr;
|
||||
}
|
||||
argv[i++] = NULL;
|
||||
assert(i==nr);
|
||||
if (clixon_proc_socket(argv, SOCK_DGRAM, &cch->cch_pid, &cch->cch_socket) < 0){
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
static int
|
||||
clixon_client_connect_ssh(clicon_handle h,
|
||||
struct clixon_client_handle *cch,
|
||||
const char *dest)
|
||||
{
|
||||
int retval = -1;
|
||||
int nr;
|
||||
int i;
|
||||
char **argv = NULL;
|
||||
char *ssh_bin = SSH_BIN;
|
||||
struct stat st = {0,};
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
nr = 5;
|
||||
if ((argv = calloc(nr, sizeof(char *))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto done;
|
||||
}
|
||||
i = 0;
|
||||
if (stat(ssh_bin, &st) < 0){
|
||||
clicon_err(OE_NETCONF, errno, "ssh binary %s", ssh_bin);
|
||||
goto done;
|
||||
}
|
||||
argv[i++] = ssh_bin;
|
||||
argv[i++] = (char*)dest;
|
||||
argv[i++] = "-s";
|
||||
argv[i++] = "netconf";
|
||||
argv[i++] = NULL;
|
||||
assert(i==nr);
|
||||
for (i=0;i<nr;i++)
|
||||
clicon_debug(1, "%s: argv[%d]:%s", __FUNCTION__, i, argv[i]);
|
||||
if (clixon_proc_socket(argv, SOCK_STREAM, &cch->cch_pid, &cch->cch_socket) < 0){
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Connect client to clixon backend according to config and return a socket
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] socktype Type of socket, internal/external/netconf/ssh
|
||||
* @param[in] dest Destination for some types
|
||||
* @retval ch Clixon session handler
|
||||
* @retval NULL Error
|
||||
* @see clixon_client_disconnect Close the socket returned here
|
||||
*/
|
||||
clixon_client_handle
|
||||
clixon_client_connect(clicon_handle h,
|
||||
clixon_client_type socktype)
|
||||
clixon_client_type socktype,
|
||||
const char *dest)
|
||||
{
|
||||
struct clixon_client_handle *cch = NULL;
|
||||
char **argv = NULL;
|
||||
int nr;
|
||||
int i;
|
||||
char *netconf_bin = NULL;
|
||||
struct stat st = {0,};
|
||||
size_t sz = sizeof(struct clixon_client_handle);
|
||||
char dbgstr[8];
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if ((cch = malloc(sz)) == NULL){
|
||||
|
|
@ -271,48 +359,19 @@ clixon_client_connect(clicon_handle h,
|
|||
goto err;
|
||||
break;
|
||||
case CLIXON_CLIENT_NETCONF:
|
||||
nr = 7;
|
||||
if (clicon_debug_get() != 0)
|
||||
nr += 2;
|
||||
if ((argv = calloc(nr, sizeof(char *))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto err;
|
||||
}
|
||||
i = 0;
|
||||
if ((netconf_bin = getenv("CLIXON_NETCONF_BIN")) == NULL)
|
||||
netconf_bin = CLIXON_NETCONF_BIN;
|
||||
if (stat(netconf_bin, &st) < 0){
|
||||
clicon_err(OE_NETCONF, errno, "netconf binary %s. Set with CLIXON_NETCONF_BIN=",
|
||||
netconf_bin);
|
||||
goto err;
|
||||
}
|
||||
argv[i++] = netconf_bin;
|
||||
argv[i++] = "-q";
|
||||
argv[i++] = "-f";
|
||||
argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE");
|
||||
argv[i++] = "-l"; /* log to syslog */
|
||||
argv[i++] = "s";
|
||||
if (clicon_debug_get() != 0){
|
||||
argv[i++] = "-D";
|
||||
snprintf(dbgstr, sizeof(dbgstr)-1, "%d", clicon_debug_get());
|
||||
argv[i++] = dbgstr;
|
||||
}
|
||||
argv[i++] = NULL;
|
||||
assert(i==nr);
|
||||
if (clixon_proc_socket(argv, &cch->cch_pid, &cch->cch_socket) < 0){
|
||||
goto err;
|
||||
}
|
||||
/* Start with encoding and hello message */
|
||||
if (clixon_client_hello(cch->cch_socket) < 0)
|
||||
if (clixon_client_connect_netconf(h, cch) < 0)
|
||||
goto err;
|
||||
break;
|
||||
#ifdef SSH_BIN
|
||||
case CLIXON_CLIENT_SSH:
|
||||
if (clixon_client_connect_ssh(h, cch, dest) < 0)
|
||||
goto err;
|
||||
#else
|
||||
clicon_err(OE_UNIX, 0, "No ssh bin");
|
||||
goto done;
|
||||
#endif
|
||||
break;
|
||||
} /* switch */
|
||||
/* lock */
|
||||
if (clixon_client_lock(cch->cch_socket, 1, "running") < 0)
|
||||
goto err;
|
||||
cch->cch_locked = 1;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%p", __FUNCTION__, cch);
|
||||
return cch;
|
||||
|
|
@ -347,13 +406,12 @@ clixon_client_disconnect(clixon_client_handle ch)
|
|||
case CLIXON_CLIENT_IPC:
|
||||
close(cch->cch_socket);
|
||||
break;
|
||||
case CLIXON_CLIENT_SSH:
|
||||
case CLIXON_CLIENT_NETCONF:
|
||||
if (clixon_proc_socket_close(cch->cch_pid,
|
||||
cch->cch_socket) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case CLIXON_CLIENT_SSH:
|
||||
break;
|
||||
}
|
||||
free(cch);
|
||||
retval = 0;
|
||||
|
|
@ -403,6 +461,7 @@ clixon_xml_bottom(cxobj *xtop,
|
|||
* @param[out] xdata XML data tree (may or may not include the intended data)
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note configurable netconf framing type, now hardwired to 0
|
||||
*/
|
||||
static int
|
||||
clixon_client_get_xdata(int sock,
|
||||
|
|
@ -445,9 +504,11 @@ clixon_client_get_xdata(int sock,
|
|||
cprintf(msg, "/>");
|
||||
}
|
||||
cprintf(msg, "</get-config></rpc>");
|
||||
if (netconf_output_encap(NETCONF_SSH_CHUNKED, msg) < 0)
|
||||
if (netconf_output_encap(0, msg) < 0) // XXX configurable session
|
||||
goto done;
|
||||
if (clicon_rpc1(sock, msg, msgret, &eof) < 0)
|
||||
if (clicon_msg_send1(sock, msg) < 0)
|
||||
goto done;
|
||||
if (clicon_msg_rcv1(sock, msgret, &eof) < 0)
|
||||
goto done;
|
||||
if (eof){
|
||||
close(sock);
|
||||
|
|
|
|||
|
|
@ -164,14 +164,17 @@ clixon_proc_sigint(int sig)
|
|||
|
||||
/*! Fork a child, exec a child and setup socket to child and return to caller
|
||||
* @param[in] argv NULL-terminated Argument vector
|
||||
* @param[in] doerr If non-zero, stderr will be directed to the pipe as well.
|
||||
* @param[out] s Socket
|
||||
* @param[in] sock_flags Socket type/flags, typically SOCK_DGRAM or SOCK_STREAM, see
|
||||
* @param[out] pid Process-id of child
|
||||
* @param[out] sock Socket
|
||||
* @retval O OK
|
||||
* @retval -1 Error.
|
||||
* @see clixon_proc_socket_close close sockets, kill child and wait for child termination
|
||||
* @see for flags usage see man sockerpair(2)
|
||||
*/
|
||||
int
|
||||
clixon_proc_socket(char **argv,
|
||||
int sock_flags,
|
||||
pid_t *pid,
|
||||
int *sock)
|
||||
{
|
||||
|
|
@ -182,12 +185,6 @@ clixon_proc_socket(char **argv,
|
|||
sigset_t oset;
|
||||
int sig = 0;
|
||||
|
||||
#ifdef __APPLE__
|
||||
int sock_flags = SOCK_DGRAM;
|
||||
#else
|
||||
int sock_flags = SOCK_DGRAM | SOCK_CLOEXEC;
|
||||
#endif
|
||||
|
||||
if (argv == NULL){
|
||||
clicon_err(OE_UNIX, EINVAL, "argv is NULL");
|
||||
goto done;
|
||||
|
|
@ -197,18 +194,6 @@ clixon_proc_socket(char **argv,
|
|||
goto done;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (fcntl(sp[0], O_CLOEXEC)) {
|
||||
clicon_err(OE_UNIX, errno, "fcntl, sp[0]");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (fcntl(sp[1], O_CLOEXEC)) {
|
||||
clicon_err(OE_UNIX, errno, "fcntl, sp[1]");
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
sigprocmask(0, NULL, &oset);
|
||||
set_signal(SIGINT, clixon_proc_sigint, &oldhandler);
|
||||
sig++;
|
||||
|
|
|
|||
|
|
@ -433,6 +433,7 @@ clicon_msg_rcv(int s,
|
|||
* @param[out] eof Set if eof encountered
|
||||
* @see netconf_input_cb()
|
||||
* @see clicon_msg_rcv using IPC message struct
|
||||
* @note only NETCONF version 1.0 EOM framing
|
||||
*/
|
||||
int
|
||||
clicon_msg_rcv1(int s,
|
||||
|
|
@ -502,7 +503,7 @@ clicon_msg_send1(int s,
|
|||
int retval = -1;
|
||||
|
||||
if (atomicio((ssize_t (*)(int, void *, size_t))write,
|
||||
s, cbuf_get(cb), cbuf_len(cb)+1) < 0){
|
||||
s, cbuf_get(cb), cbuf_len(cb)) < 0){
|
||||
clicon_err(OE_CFG, errno, "atomicio");
|
||||
clicon_log(LOG_WARNING, "%s: write: %s", __FUNCTION__, strerror(errno));
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ main(int argc,
|
|||
int retval = -1;
|
||||
clixon_handle h = NULL; /* clixon handle */
|
||||
clixon_client_handle ch = NULL; /* clixon client handle */
|
||||
int s;
|
||||
|
||||
clicon_log_init("client", LOG_DEBUG, CLICON_LOG_STDERR); // debug
|
||||
clicon_debug_init($debug, NULL); // debug
|
||||
|
|
@ -95,9 +96,11 @@ main(int argc,
|
|||
if ((h = clixon_client_init("$cfg")) == NULL)
|
||||
return -1;
|
||||
/* Make a connection over netconf or ssh/netconf */
|
||||
if ((ch = clixon_client_connect(h, CLIXON_CLIENT_NETCONF)) == NULL)
|
||||
if ((ch = clixon_client_connect(h, CLIXON_CLIENT_NETCONF, NULL)) == NULL)
|
||||
return -1;
|
||||
s = clixon_client_socket_get(ch);
|
||||
if (clixon_client_hello(s, 0) < 0)
|
||||
return -1;
|
||||
|
||||
/* Here are read functions depending on an example YANG
|
||||
* (Need an example YANG and XML input to confd)
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue