Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Linux: Manage routes in a routing table dedicated to ZET. #892

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
3 changes: 2 additions & 1 deletion lib/ziti-tunnel-cbs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ target_compile_definitions(ziti-tunnel-sdk-c
add_library(ziti-tunnel-cbs-c STATIC
ziti_tunnel_cbs.c
ziti_hosting.c
ziti_hosting_socket.c
ziti_tunnel_ctrl.c
ziti_instance.h
ziti_dns.c
Expand Down Expand Up @@ -54,4 +55,4 @@ install(TARGETS ziti-tunnel-cbs-c

if(ZITI_TUNNEL_BUILD_TESTS)
add_subdirectory(tests)
endif()
endif()
158 changes: 121 additions & 37 deletions lib/ziti-tunnel-cbs/ziti_hosting.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@

#define KEEPALIVE_DELAY 60

#ifndef WIN32
# define closesocket(s) close(s)
#endif /* WIN32 */

/**
* Override socket creation
*/
extern int ziti_tunnel_hosting_socket(/* out */ uv_os_sock_t *, /* in */ const struct addrinfo *);

/********** hosting **********/
static void on_bridge_close(uv_handle_t *handle);

Expand All @@ -54,13 +63,15 @@
uv_tcp_t tcp;
uv_udp_t udp;
} server;
struct addrinfo *bind_address;
};

static void hosted_io_context_free(hosted_io_context io) {
if (io) {
if (io->app_data) {
free_tunneler_app_data_ptr(io->app_data);
}
uv_freeaddrinfo(io->bind_address);
free(io);
}
}
Expand Down Expand Up @@ -435,9 +446,21 @@
return port_from_config;
}

static int do_bind(hosted_io_context io, const char *addr, int socktype) {
static int resolve_bind_address(hosted_io_context io, const tunneler_app_data *app_data, int socktype) {
// if app_data includes source ip[:port], verify that it is allowed before attempting to bind
const char *addr;

if (!app_data || !app_data->source_addr || !app_data->source_addr[0])
return 0;

addr = app_data->source_addr;

// split out the ip and port if port was specified
char *src_ip = strdup(addr);
if (src_ip == NULL)
return UV_ENOMEM;

// XXX Does this work for IPv6 addresses?
char *port = strchr(src_ip, ':');
if (port != NULL) {
*port = '\0';
Expand All @@ -446,6 +469,7 @@

uv_getaddrinfo_t ai_req = {0};
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
hints.ai_protocol = get_protocol_id(io->computed_dst_protocol);
hints.ai_socktype = socktype;
Expand All @@ -456,7 +480,7 @@
if (uv_err != 0) {
ZITI_LOG(ERROR, "hosted_service[%s], client[%s]: getaddrinfo(%s) failed: %s",
io->service->service_name, io->client_identity, addr, uv_strerror(uv_err));
return -1;
return uv_err;
}

if (ai_req.addrinfo->ai_next != NULL) {
Expand All @@ -469,36 +493,38 @@
if (!address_match(&src_za, &io->service->allowed_source_addresses)) {
ZITI_LOG(ERROR, "hosted_service[%s], client[%s] client requested source IP %s is not allowed",
io->service->service_name, io->client_identity, addr);
return -1;
uv_err = UV_EPERM;
goto free_and_exit;
}

switch (hints.ai_protocol) {
case IPPROTO_TCP:
uv_err = uv_tcp_bind(&io->server.tcp, ai_req.addrinfo->ai_addr, 0);
break;
case IPPROTO_UDP:
uv_err = uv_udp_bind(&io->server.udp, ai_req.addrinfo->ai_addr, 0);
break;
default:
ZITI_LOG(ERROR, "hosted_service[%s] client[%s] unsupported protocol %d when binding source address",
ZITI_LOG(ERROR, "hosted_service[%s] client[%s] unsupported protocol %d",
io->service->service_name, io->client_identity, hints.ai_protocol);
uv_err = UV_EINVAL;
uv_err = UV_EPROTONOSUPPORT;
goto free_and_exit;
}

uv_freeaddrinfo(ai_req.addrinfo);

if (uv_err != 0) {
ZITI_LOG(ERROR, "hosted_service[%s] client[%s]: bind failed: %s", io->service->service_name,
io->client_identity, uv_strerror(uv_err));
return -1;
}
io->bind_address = ai_req.addrinfo;

return 0;

free_and_exit:
uv_freeaddrinfo(ai_req.addrinfo);
return uv_err;
}

static hosted_io_context hosted_io_context_new(struct hosted_service_ctx_s *service_ctx, ziti_connection client,
tunneler_app_data *app_data, const char *dst_protocol, const char *dst_ip_or_hn, const char *dst_port) {
hosted_io_context io = calloc(1, sizeof(struct hosted_io_ctx_s));

if (!io)
return NULL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is worth logging an ERROR message


io->service = service_ctx;

// include underlay details in client identity if available
Expand Down Expand Up @@ -526,25 +552,21 @@
io->server.udp.data = io;
break;
default:
ZITI_LOG(ERROR, "hosted_service[%s] client[%s] unsupported protocol '%s''", service_ctx->service_name,
io->client_identity, dst_protocol);
free(io);
return NULL;
uv_err = UV_EPROTONOSUPPORT;
}

if (uv_err != 0) {
ZITI_LOG(ERROR, "hosted_service[%s] client[%s] dst[%s:%s:%s] failed to initialize underlay handle: %s",
service_ctx->service_name, io->client_identity, dst_protocol, dst_ip_or_hn, dst_port, uv_strerror(uv_err));
free(io);
return NULL;
}

// uv handle has been initialized and must be closed before freeing `io` now.

// if app_data includes source ip[:port], verify that it is allowed before attempting to bind
if (app_data && app_data->source_addr && app_data->source_addr[0] != '\0') {
if (do_bind(io, app_data->source_addr, socktype) != 0) {
hosted_server_close(io);
return NULL;
}
if (resolve_bind_address(io, app_data, socktype) != 0) {
hosted_server_close(io);
return NULL;
}

// success. now set references to ziti connection and app_data so cleanup happens in ziti_conn_close_cb
Expand Down Expand Up @@ -637,10 +659,14 @@
ZITI_LOG(INFO, "hosted_service[%s] client[%s] dst_addr[%s:%s:%s]: incoming connection",
service_ctx->service_name, io->client_identity, protocol, ip_or_hn, port);

/**
* make remote_address compatible with supplied bind_address
*/
struct addrinfo hints = {0};
hints.ai_family = io->bind_address ? io->bind_address->ai_family : AF_UNSPEC;
hints.ai_protocol = protocol_number;
hints.ai_socktype = protocol_number == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM;
hints.ai_flags = AI_NUMERICSERV;
hints.ai_flags = AI_V4MAPPED | AI_NUMERICSERV;
if (is_ip) hints.ai_flags |= AI_NUMERICHOST;
ziti_conn_set_data(clt, io);

Expand Down Expand Up @@ -670,6 +696,65 @@
}
}

static int do_tcp_connect(hosted_io_context io, const struct addrinfo *remote_address, uv_connect_cb cb)
{
uv_connect_t *request;
uv_os_sock_t sock;
int uv_err;

if ((request = calloc(1, sizeof *request)) == NULL)
return UV_ENOMEM;

/**
* remote_address is made compatible with bind_address
*/
uv_err = ziti_tunnel_hosting_socket(&sock, remote_address);
if (uv_err < 0 && uv_err != UV_ENOSYS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please log errors for each of the failure paths in this function. It will make it much easier to support users if we know exactly which function failed when something goes wrong.

goto free_request_and_exit;

if (uv_err == 0 && (uv_err = uv_tcp_open(&io->server.tcp, sock)) < 0) {
(void) closesocket(sock);
goto free_request_and_exit;
}

if (io->bind_address && (uv_err = uv_tcp_bind(&io->server.tcp, io->bind_address->ai_addr, 0)))
goto free_request_and_exit;

uv_err = uv_tcp_connect(request, &io->server.tcp, remote_address->ai_addr, cb);
if (uv_err < 0)
goto free_request_and_exit;

return 0;

free_request_and_exit:

free(request);
return uv_err;
}

static int do_udp_connect(hosted_io_context io, const struct addrinfo *remote_address)
{
uv_os_sock_t sock;
int uv_err;

uv_err = ziti_tunnel_hosting_socket(&sock, remote_address);
if (uv_err < 0 && uv_err != UV_ENOSYS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please log errors for each of the failure paths in this function, also.

return uv_err;

if (uv_err == 0 && (uv_err = uv_udp_open(&io->server.udp, sock)) < 0) {
(void) closesocket(sock);
return uv_err;
}

if (io->bind_address && (uv_err = uv_udp_bind(&io->server.udp, io->bind_address->ai_addr, 0)) < 0)
return uv_err;

if ((uv_err = udp_connect(&io->server.udp, remote_address->ai_addr)) < 0)

Check failure on line 752 in lib/ziti-tunnel-cbs/ziti_hosting.c

View workflow job for this annotation

GitHub Actions / build macOS-x64

call to undeclared function 'udp_connect'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]

Check failure on line 752 in lib/ziti-tunnel-cbs/ziti_hosting.c

View workflow job for this annotation

GitHub Actions / build macOS-arm64

call to undeclared function 'udp_connect'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be uv_udp_connect?

return uv_err;

return 0;
}

static void on_hosted_client_connect_resolved(uv_getaddrinfo_t* ai_req, int status, struct addrinfo* res) {
hosted_io_context io = ai_req->data;
if (io == NULL) {
Expand Down Expand Up @@ -711,22 +796,18 @@

switch (res->ai_protocol) {
case IPPROTO_TCP:
{
uv_connect_t *c = malloc(sizeof(uv_connect_t));
uv_err = uv_tcp_connect(c, &io->server.tcp, res->ai_addr, on_hosted_tcp_server_connect_complete);
if (uv_err != 0) {
ZITI_LOG(ERROR, "hosted_service[%s], client[%s]: uv_tcp_connect failed: %s",
io->service->service_name, io->client_identity, uv_strerror(uv_err));
hosted_server_close(io);
free(c);
}
uv_err = do_tcp_connect(io, res, on_hosted_tcp_server_connect_complete);
if (uv_err != 0) {
ZITI_LOG(ERROR, "hosted_service[%s] client[%s]: TCP connection failed: %s",
io->service->service_name, io->client_identity, uv_strerror(uv_err));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should omit the uv error string here, especially if the specific failures are logged in do_tcp_connect

hosted_server_close(io);
}
break;
case IPPROTO_UDP:
uv_err = uv_udp_connect(&io->server.udp, res->ai_addr);
uv_err = do_udp_connect(io, res);
if (uv_err != 0) {
ZITI_LOG(ERROR, "hosted_service[%s], client[%s]: uv_udp_connect failed: %s",
io->service->service_name, io->client_identity, uv_strerror(uv_err));
ZITI_LOG(ERROR, "hosted_service[%s] client[%s]: uv_udp_open failed: %s",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to log a generic message here (like you did for the TCP case) rather than mention a function that may (or may not) have been the cause of the failure.

io->service->service_name, io->client_identity, uv_strerror(uv_err));
hosted_server_close(io);
} else if (ziti_accept(io->client, on_hosted_client_connect_complete, NULL) != ZITI_OK) {
ZITI_LOG(ERROR, "ziti_accept failed");
Expand All @@ -737,6 +818,9 @@

uv_freeaddrinfo(res);
free(ai_req);

uv_freeaddrinfo(io->bind_address);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it sufficient to let hosted_io_context_free do this?

io->bind_address = NULL;
}

/** called by ziti SDK when a hosted service listener is ready */
Expand Down
11 changes: 11 additions & 0 deletions lib/ziti-tunnel-cbs/ziti_hosting_socket.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <uv.h>

extern int ziti_tunnel_hosting_socket(uv_os_sock_t *, const struct addrinfo *ai);

int
ziti_tunnel_hosting_socket(uv_os_sock_t *sock, const struct addrinfo *ai)
{
(void) sock;
(void) ai;
return UV_ENOSYS;
}
5 changes: 4 additions & 1 deletion programs/ziti-edge-tunnel/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
project(ziti-edge-tunnel)

include(CheckFunctionExists)

if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
set(NETIF_DRIVER_SOURCE netif_driver/darwin/utun.c)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL Linux)
set(NETIF_DRIVER_SOURCE netif_driver/linux/tun.c netif_driver/linux/resolvers.c netif_driver/linux/utils.c)
set(NETIF_DRIVER_SOURCE netif_driver/linux/tun.c netif_driver/linux/resolvers.c netif_driver/linux/utils.c netif_driver/linux/libiproute.c netif_driver/linux/capability.c)
check_function_exists(capget ZITI_TUNNELER_SDK_HAVE_CAPGET)
endif()

if(CMAKE_SYSTEM_NAME STREQUAL Windows)
Expand Down
Loading
Loading