Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add support for the local-binding-supported YANG feature #500

Merged
merged 3 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ The second API is [nc_server_config_load_modules](https://netopeer.liberouter.or
- **ietf-netconf-server**: ssh-listen ✔, tls-listen ✔, ssh-call-home ✔, tls-call-home ✔, central-netconf-server-supported ✔,
- **ietf-ssh-common**: transport-params ✔, ssh-x509-certs ✘, public-key-generation ✘,
- **ietf-ssh-server**: local-users-supported **?**, local-user-auth-publickey ✔, local-user-auth-password ✔, local-user-auth-none ✔, ssh-server-keepalives ✘, local-user-auth-hostbased ✘,
- **ietf-tcp-client**: tcp-client-keepalives ✔, proxy-connect ✘, socks5-gss-api ✘, socks5-username-password ✘, local-binding-supported ,
- **ietf-tcp-client**: tcp-client-keepalives ✔, proxy-connect ✘, socks5-gss-api ✘, socks5-username-password ✘, local-binding-supported ,
- **ietf-tcp-common**: transport-params ✔, ssh-x509-certs ✘, public-key-generation ✘,
- **ietf-tcp-server**: tcp-server-keepalives ✔,
- **ietf-tls-common**: tls10 ✔, tls11 ✔, tls12 ✔, tls13 ✔, hello-params ✔, public-key-generation ✘,
Expand Down
83 changes: 66 additions & 17 deletions src/server_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,8 @@ nc_server_config_ch_del_endpt(struct nc_ch_client *ch_client, struct nc_ch_endpt
free(ch_endpt->name);

#ifdef NC_ENABLED_SSH_TLS
free(ch_endpt->address);
free(ch_endpt->src_addr);
free(ch_endpt->dst_addr);
if (ch_endpt->sock_pending > -1) {
close(ch_endpt->sock_pending);
ch_endpt->sock_pending = -1;
Expand Down Expand Up @@ -1338,19 +1339,19 @@ nc_server_config_tls(const struct lyd_node *node, NC_OPERATION op)
return ret;
}

/* mandatory leaf */
/* leaf */
static int
nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
struct nc_endpt *endpt;
struct nc_bind *bind;
int ret = 0;

(void) op;
struct nc_ch_client *ch_client = NULL;
struct nc_ch_endpt *ch_endpt;

assert(!strcmp(LYD_NAME(node), "local-address"));

if (equal_parent_name(node, 5, "listen")) {
if (is_listen(node) && equal_parent_name(node, 1, "tcp-server-parameters")) {
if (nc_server_config_get_endpt(node, &endpt, &bind)) {
ret = 1;
goto cleanup;
Expand All @@ -1364,23 +1365,49 @@ nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op)
if (ret) {
goto cleanup;
}
} else if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
/* LOCK */
if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
/* to avoid unlock on fail */
return 1;
}

if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) {
ret = 1;
goto cleanup;
}

if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
free(ch_endpt->src_addr);
ch_endpt->src_addr = strdup(lyd_get_value(node));
NC_CHECK_ERRMEM_GOTO(!ch_endpt->src_addr, ret = 1, cleanup);
} else if (op == NC_OP_DELETE) {
free(ch_endpt->src_addr);
ch_endpt->src_addr = NULL;
}
}

cleanup:
if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
/* UNLOCK */
nc_ch_client_unlock(ch_client);
}
return ret;
}

/* leaf with default value */
static int
nc_server_config_local_port(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
struct nc_endpt *endpt;
struct nc_bind *bind;
int ret = 0;
struct nc_ch_client *ch_client = NULL;
struct nc_ch_endpt *ch_endpt;

assert(!strcmp(LYD_NAME(node), "local-port"));

if (equal_parent_name(node, 5, "listen")) {
if (is_listen(node) && equal_parent_name(node, 1, "tcp-server-parameters")) {
if (nc_server_config_get_endpt(node, &endpt, &bind)) {
ret = 1;
goto cleanup;
Expand All @@ -1397,9 +1424,31 @@ nc_server_config_local_port(const struct lyd_node *node, NC_OPERATION op)
if (ret) {
goto cleanup;
}
} else if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
/* LOCK */
if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) {
/* to avoid unlock on fail */
return 1;
}

if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) {
ret = 1;
goto cleanup;
}

if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
ch_endpt->src_port = ((struct lyd_node_term *)node)->value.uint16;
} else if (op == NC_OP_DELETE) {
/* delete -> set to default */
ch_endpt->src_port = 0;
}
}

cleanup:
if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) {
/* UNLOCK */
nc_ch_client_unlock(ch_client);
}
return ret;
}

Expand Down Expand Up @@ -3359,12 +3408,12 @@ nc_server_config_remote_address(const struct lyd_node *node, NC_OPERATION op)
}

if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
free(ch_endpt->address);
ch_endpt->address = strdup(lyd_get_value(node));
NC_CHECK_ERRMEM_GOTO(!ch_endpt->address, ret = 1, cleanup);
free(ch_endpt->dst_addr);
ch_endpt->dst_addr = strdup(lyd_get_value(node));
NC_CHECK_ERRMEM_GOTO(!ch_endpt->dst_addr, ret = 1, cleanup);
} else {
free(ch_endpt->address);
ch_endpt->address = NULL;
free(ch_endpt->dst_addr);
ch_endpt->dst_addr = NULL;
}

cleanup:
Expand Down Expand Up @@ -3394,9 +3443,9 @@ nc_server_config_remote_port(const struct lyd_node *node, NC_OPERATION op)
}

if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
ch_endpt->port = ((struct lyd_node_term *)node)->value.uint16;
ch_endpt->dst_port = ((struct lyd_node_term *)node)->value.uint16;
} else {
ch_endpt->port = 0;
ch_endpt->dst_port = 0;
}

cleanup:
Expand Down Expand Up @@ -3846,8 +3895,8 @@ nc_server_config_load_modules(struct ly_ctx **ctx)
const char *ietf_tcp_common[] = {"keepalives-supported", NULL};
/* all features */
const char *ietf_tcp_server[] = {"tcp-server-keepalives", NULL};
/* no proxy-connect, socks5-gss-api, socks5-username-password, local-binding-supported ? */
const char *ietf_tcp_client[] = {"tcp-client-keepalives", NULL};
/* no proxy-connect, socks5-gss-api, socks5-username-password */
const char *ietf_tcp_client[] = {"local-binding-supported", "tcp-client-keepalives", NULL};
/* no ssh-x509-certs, public-key-generation */
const char *ietf_ssh_common[] = {"transport-params", NULL};
/* no ssh-server-keepalives and local-user-auth-hostbased */
Expand Down
42 changes: 32 additions & 10 deletions src/session_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1593,9 +1593,10 @@ nc_saddr2str(const struct sockaddr *saddr, char **str_ip, uint16_t *port)
* @return Connected socket or -1 on error.
*/
static int
sock_connect(int timeout_ms, int *sock_pending, struct addrinfo *res, const struct nc_keepalives *ka)
sock_connect(const char *src_addr, uint16_t src_port, int timeout_ms, int *sock_pending, struct addrinfo *res,
const struct nc_keepalives *ka)
{
int flags, ret, error;
int flags, ret, error, opt;
int sock = -1;
struct pollfd fds = {0};
socklen_t len = sizeof(int);
Expand Down Expand Up @@ -1624,6 +1625,21 @@ sock_connect(int timeout_ms, int *sock_pending, struct addrinfo *res, const stru
ERR(NULL, "fcntl() failed (%s).", strerror(errno));
goto cleanup;
}

/* bind the socket to a specific address/port to make the connection from (CH only) */
if (src_addr || src_port) {
/* enable address reuse, so that we're able to bind this address again when the CH conn is dropped and retried */
opt = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) {
ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno));
goto cleanup;
}

if (nc_sock_bind_inet(sock, src_addr, src_port, (res->ai_family == AF_INET) ? 1 : 0)) {
goto cleanup;
}
}

/* non-blocking connect! */
if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
if (errno != EINPROGRESS) {
Expand Down Expand Up @@ -1688,35 +1704,36 @@ sock_connect(int timeout_ms, int *sock_pending, struct addrinfo *res, const stru
}

int
nc_sock_connect(const char *host, uint16_t port, int timeout_ms, struct nc_keepalives *ka, int *sock_pending, char **ip_host)
nc_sock_connect(const char *src_addr, uint16_t src_port, const char *dst_addr, uint16_t dst_port, int timeout_ms,
struct nc_keepalives *ka, int *sock_pending, char **ip_host)
{
int i, opt;
int sock = sock_pending ? *sock_pending : -1;
struct addrinfo hints, *res_list = NULL, *res;
char port_s[6]; /* length of string representation of short int */
char dst_port_str[6]; /* length of string representation of short int */
struct sockaddr_storage saddr;
socklen_t addr_len = sizeof saddr;

*ip_host = NULL;

DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout_ms, sock);
DBG(NULL, "nc_sock_connect(%s, %u, %s, %u, %d, %d)", src_addr, src_port, dst_addr, dst_port, timeout_ms, sock);

/* no pending socket */
if (sock == -1) {
/* connect to a server */
snprintf(port_s, 6, "%u", port);
snprintf(dst_port_str, 6, "%u", dst_port);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
i = getaddrinfo(host, port_s, &hints, &res_list);
i = getaddrinfo(dst_addr, dst_port_str, &hints, &res_list);
if (i != 0) {
ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
goto error;
}

for (res = res_list; res != NULL; res = res->ai_next) {
sock = sock_connect(timeout_ms, sock_pending, res, ka);
sock = sock_connect(src_addr, src_port, timeout_ms, sock_pending, res, ka);
if (sock == -1) {
if (!sock_pending || (*sock_pending == -1)) {
/* try the next resource */
Expand All @@ -1726,7 +1743,12 @@ nc_sock_connect(const char *host, uint16_t port, int timeout_ms, struct nc_keepa
break;
}
}
VRB(NULL, "Successfully connected to %s:%s over %s.", host, port_s, (res->ai_family == AF_INET6) ? "IPv6" : "IPv4");

if (res->ai_family == AF_INET) {
VRB(NULL, "Successfully connected to %s:%s over IPv4.", dst_addr, dst_port_str);
} else {
VRB(NULL, "Successfully connected to [%s]:%s over IPv6.", dst_addr, dst_port_str);
}

opt = 1;
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) {
Expand All @@ -1744,7 +1766,7 @@ nc_sock_connect(const char *host, uint16_t port, int timeout_ms, struct nc_keepa
} else {
/* try to get a connection with the pending socket */
assert(sock_pending);
sock = sock_connect(timeout_ms, sock_pending, NULL, ka);
sock = sock_connect(src_addr, src_port, timeout_ms, sock_pending, NULL, ka);

if (sock > 0) {
if (getpeername(sock, (struct sockaddr *)&saddr, &addr_len)) {
Expand Down
4 changes: 2 additions & 2 deletions src/session_client_ssh.c
Original file line number Diff line number Diff line change
Expand Up @@ -1606,7 +1606,7 @@ _nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepal
}

/* create and connect socket */
sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host);
sock = nc_sock_connect(NULL, 0, host, port, -1, ka, NULL, &ip_host);
if (sock == -1) {
ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
free(host);
Expand Down Expand Up @@ -1762,7 +1762,7 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
}

/* create and assign communication socket */
sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
sock = nc_sock_connect(NULL, 0, host, port, -1, &client_opts.ka, NULL, &ip_host);
if (sock == -1) {
ERR(session, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
goto fail;
Expand Down
2 changes: 1 addition & 1 deletion src/session_client_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
session->status = NC_STATUS_STARTING;

/* create and assign socket */
sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
sock = nc_sock_connect(NULL, 0, host, port, -1, &client_opts.ka, NULL, &ip_host);
if (sock == -1) {
ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
goto fail;
Expand Down
29 changes: 23 additions & 6 deletions src/session_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -470,8 +470,12 @@ struct nc_server_opts {
char *referenced_endpt_name;
#endif /* NC_ENABLED_SSH_TLS */
NC_TRANSPORT_IMPL ti;
char *address;
uint16_t port;

char *src_addr; /**< IP address to bind to when connecting to a Call Home client. */
uint16_t src_port; /**< Port to bind to when connecting to a Call Home client. */
char *dst_addr; /**< IP address of the Call Home client. */
uint16_t dst_port; /**< Port of the Call Home client. */

int sock_pending;
struct nc_keepalives ka;

Expand Down Expand Up @@ -896,20 +900,33 @@ int nc_ctx_check_and_fill(struct nc_session *session);
*/
NC_MSG_TYPE nc_handshake_io(struct nc_session *session);

/**
* @brief Bind a socket to an address and a port.
*
* @param[in] sock Socket to bind.
* @param[in] address Address to bind to.
* @param[in] port Port to bind to.
* @param[in] is_ipv4 Whether the address is IPv4 or IPv6.
* @return 0 on success, -1 on error.
*/
int nc_sock_bind_inet(int sock, const char *address, uint16_t port, int is_ipv4);

/**
* @brief Create a socket connection.
*
* @param[in] host Hostname to connect to.
* @param[in] port Port to connect on.
* @param[in] src_addr Address to connect from.
* @param[in] src_port Port to connect from.
* @param[in] dst_addr Address to connect to.
* @param[in] dst_port Port to connect to.
* @param[in] timeout_ms Timeout in ms for blocking the connect + select call (-1 for infinite).
* @param[in] ka Keepalives parameters.
* @param[in,out] sock_pending Previous pending socket. If set, equal to -1, and the connection is still in progress
* after @p timeout, it is set to the pending socket but -1 is returned. If NULL, the socket is closed on timeout.
* @param[out] ip_host Optional parameter with string IP address of the connected host.
* @return Connected socket or -1 on error.
*/
int nc_sock_connect(const char *host, uint16_t port, int timeout_ms, struct nc_keepalives *ka, int *sock_pending,
char **ip_host);
int nc_sock_connect(const char *src_addr, uint16_t src_port, const char *dst_addr, uint16_t dst_port, int timeout_ms,
struct nc_keepalives *ka, int *sock_pending, char **ip_host);

/**
* @brief Accept a new socket connection.
Expand Down
Loading