blob: b01bcfef8f6729593ea943b52e94cdf1d7357f68 [file] [log] [blame]
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <fcntl.h>
#include <zephyr/logging/log.h>
#include <zephyr/net/dummy.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/offloaded_netdev.h>
#include <zephyr/net/socket.h>
#include <sockets_internal.h>
#include <zephyr/sys/fdtable.h>
#include <zephyr/ztest.h>
LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
/* Generic test offload API */
#define OFFLOAD_1 0
#define OFFLOAD_2 1
#define OFFLOAD_COUNT 2
static struct test_socket_calls {
bool socket_called;
bool close_called;
bool ioctl_called;
bool shutdown_called;
bool bind_called;
bool connect_called;
bool listen_called;
bool accept_called;
bool sendto_called;
bool recvfrom_called;
bool getsockopt_called;
bool setsockopt_called;
bool sendmsg_called;
bool getsockname_called;
bool getpeername_called;
} test_socket_ctx[OFFLOAD_COUNT];
static int test_sock = -1;
static ssize_t offload_read(void *obj, void *buffer, size_t count)
{
ARG_UNUSED(obj);
ARG_UNUSED(buffer);
ARG_UNUSED(count);
return 0;
}
static ssize_t offload_write(void *obj, const void *buffer, size_t count)
{
ARG_UNUSED(obj);
ARG_UNUSED(buffer);
ARG_UNUSED(count);
return 0;
}
static int offload_close(void *obj)
{
struct test_socket_calls *ctx = obj;
ctx->close_called = true;
return 0;
}
static int offload_ioctl(void *obj, unsigned int request, va_list args)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(request);
ARG_UNUSED(args);
ctx->ioctl_called = true;
return 0;
}
static int offload_shutdown(void *obj, int how)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(how);
ctx->shutdown_called = true;
return 0;
}
static int offload_bind(void *obj, const struct sockaddr *addr,
socklen_t addrlen)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(addr);
ARG_UNUSED(addrlen);
ctx->bind_called = true;
return 0;
}
static int offload_connect(void *obj, const struct sockaddr *addr,
socklen_t addrlen)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(addr);
ARG_UNUSED(addrlen);
ctx->connect_called = true;
return 0;
}
static int offload_listen(void *obj, int backlog)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(backlog);
ctx->listen_called = true;
return 0;
}
static int offload_accept(void *obj, struct sockaddr *addr, socklen_t *addrlen)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(addr);
ARG_UNUSED(addrlen);
ctx->accept_called = true;
return 0;
}
static ssize_t offload_sendto(void *obj, const void *buf, size_t len,
int flags, const struct sockaddr *dest_addr,
socklen_t addrlen)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(buf);
ARG_UNUSED(len);
ARG_UNUSED(flags);
ARG_UNUSED(dest_addr);
ARG_UNUSED(addrlen);
ctx->sendto_called = true;
return len;
}
static ssize_t offload_sendmsg(void *obj, const struct msghdr *msg, int flags)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(msg);
ARG_UNUSED(flags);
ctx->sendmsg_called = true;
return 0;
}
static ssize_t offload_recvfrom(void *obj, void *buf, size_t max_len,
int flags, struct sockaddr *src_addr,
socklen_t *addrlen)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(buf);
ARG_UNUSED(max_len);
ARG_UNUSED(flags);
ARG_UNUSED(src_addr);
ARG_UNUSED(addrlen);
ctx->recvfrom_called = true;
return 0;
}
static int offload_getsockopt(void *obj, int level, int optname,
void *optval, socklen_t *optlen)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(level);
ARG_UNUSED(optname);
ARG_UNUSED(optval);
ARG_UNUSED(optlen);
ctx->getsockopt_called = true;
return 0;
}
static int offload_setsockopt(void *obj, int level, int optname,
const void *optval, socklen_t optlen)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(level);
ARG_UNUSED(optname);
ARG_UNUSED(optval);
ARG_UNUSED(optlen);
ctx->setsockopt_called = true;
return 0;
}
static int offload_getpeername(void *obj, struct sockaddr *addr,
socklen_t *addrlen)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(addr);
ARG_UNUSED(addrlen);
ctx->getpeername_called = true;
return 0;
}
static int offload_getsockname(void *obj, struct sockaddr *addr,
socklen_t *addrlen)
{
struct test_socket_calls *ctx = obj;
ARG_UNUSED(addr);
ARG_UNUSED(addrlen);
ctx->getsockname_called = true;
return 0;
}
/* Offloaded interface 1 - high priority */
#define SOCKET_OFFLOAD_PRIO_HIGH 10
static const struct socket_op_vtable offload_1_socket_fd_op_vtable = {
.fd_vtable = {
.read = offload_read,
.write = offload_write,
.close = offload_close,
.ioctl = offload_ioctl,
},
.shutdown = offload_shutdown,
.bind = offload_bind,
.connect = offload_connect,
.listen = offload_listen,
.accept = offload_accept,
.sendto = offload_sendto,
.recvfrom = offload_recvfrom,
.getsockopt = offload_getsockopt,
.setsockopt = offload_setsockopt,
.sendmsg = offload_sendmsg,
.getsockname = offload_getsockname,
.getpeername = offload_getpeername,
};
int offload_1_socket(int family, int type, int proto)
{
int fd = z_reserve_fd();
if (fd < 0) {
return -1;
}
z_finalize_fd(fd, &test_socket_ctx[OFFLOAD_1],
(const struct fd_op_vtable *)
&offload_1_socket_fd_op_vtable);
test_socket_ctx[OFFLOAD_1].socket_called = true;
return fd;
}
static bool offload_1_is_supported(int family, int type, int proto)
{
return true;
}
NET_SOCKET_OFFLOAD_REGISTER(offloaded_1, SOCKET_OFFLOAD_PRIO_HIGH, AF_UNSPEC,
offload_1_is_supported, offload_1_socket);
static void offloaded_1_iface_init(struct net_if *iface)
{
net_if_socket_offload_set(iface, offload_1_socket);
}
static struct offloaded_if_api offloaded_1_if_api = {
.iface_api.init = offloaded_1_iface_init,
};
NET_DEVICE_OFFLOAD_INIT(offloaded_1, "offloaded_1", NULL, NULL,
NULL, NULL, 0, &offloaded_1_if_api, 1500);
/* Offloaded interface 2 - low priority */
#define SOCKET_OFFLOAD_PRIO_LOW 20
static const struct socket_op_vtable offload_2_socket_fd_op_vtable = {
.fd_vtable = {
.read = offload_read,
.write = offload_write,
.close = offload_close,
.ioctl = offload_ioctl,
},
.shutdown = offload_shutdown,
.bind = offload_bind,
.connect = offload_connect,
.listen = offload_listen,
.accept = offload_accept,
.sendto = offload_sendto,
.recvfrom = offload_recvfrom,
.getsockopt = offload_getsockopt,
.setsockopt = offload_setsockopt,
.sendmsg = offload_sendmsg,
.getsockname = offload_getsockname,
.getpeername = offload_getpeername,
};
int offload_2_socket(int family, int type, int proto)
{
int fd = z_reserve_fd();
if (fd < 0) {
return -1;
}
z_finalize_fd(fd, &test_socket_ctx[OFFLOAD_2],
(const struct fd_op_vtable *)
&offload_2_socket_fd_op_vtable);
test_socket_ctx[OFFLOAD_2].socket_called = true;
return fd;
}
static bool offload_2_is_supported(int family, int type, int proto)
{
return true;
}
NET_SOCKET_OFFLOAD_REGISTER(offloaded_2, SOCKET_OFFLOAD_PRIO_HIGH, AF_UNSPEC,
offload_2_is_supported, offload_2_socket);
static void offloaded_2_iface_init(struct net_if *iface)
{
net_if_socket_offload_set(iface, offload_2_socket);
}
static struct offloaded_if_api offloaded_2_if_api = {
.iface_api.init = offloaded_2_iface_init,
};
NET_DEVICE_OFFLOAD_INIT(offloaded_2, "offloaded_2", NULL, NULL,
NULL, NULL, 0, &offloaded_2_if_api, 1500);
/* Native dummy interface */
static uint8_t lladdr[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
static struct in_addr in4addr_my = { { { 192, 0, 2, 1 } } };
static K_SEM_DEFINE(test_native_send_called, 0, 1);
static void dummy_native_iface_init(struct net_if *iface)
{
net_if_set_link_addr(iface, lladdr, 6, NET_LINK_DUMMY);
net_if_ipv4_addr_add(iface, &in4addr_my, NET_ADDR_MANUAL, 0);
}
static int dummy_native_dev_send(const struct device *dev, struct net_pkt *pkt)
{
ARG_UNUSED(dev);
ARG_UNUSED(pkt);
k_sem_give(&test_native_send_called);
return 0;
}
static const struct dummy_api dummy_native_dev_api = {
.iface_api.init = dummy_native_iface_init,
.send = dummy_native_dev_send,
};
NET_DEVICE_INIT(dummy_native, "dummy_native", NULL, NULL, NULL,
NULL, 0, &dummy_native_dev_api, DUMMY_L2,
NET_L2_GET_CTX_TYPE(DUMMY_L2), 1500);
/* Actual tests */
static const struct sockaddr_in test_peer_addr = {
.sin_family = AF_INET,
.sin_addr = { { { 192, 0, 0, 2 } } },
.sin_port = 1234
};
static void test_result_reset(void)
{
memset(test_socket_ctx, 0, sizeof(test_socket_ctx));
k_sem_reset(&test_native_send_called);
}
static void test_socket_setup_udp(void *dummy)
{
ARG_UNUSED(dummy);
test_result_reset();
test_sock = zsock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
zassert_true(test_sock >= 0, "Failed to create socket");
zassert_false(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should'nt have been dispatched yet");
}
static void test_socket_setup_tls(void *dummy)
{
ARG_UNUSED(dummy);
test_result_reset();
test_sock = zsock_socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2);
zassert_true(test_sock >= 0, "Failed to create socket");
zassert_false(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should'nt have been dispatched yet");
}
static void test_socket_teardown(void *dummy)
{
ARG_UNUSED(dummy);
int ret = zsock_close(test_sock);
test_sock = -1;
zassert_equal(0, ret, "close() failed");
}
/* Verify that socket is not dispatched when close() is called immediately after
* creating dispatcher socket.
*/
ZTEST(net_socket_offload_close, test_close_not_bound)
{
int ret = zsock_close(test_sock);
test_sock = -1;
zassert_equal(0, ret, "close() failed");
zassert_false(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should'nt have been dispatched");
zassert_false(test_socket_ctx[OFFLOAD_1].close_called,
"close() should'nt have been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on ioctl() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_fcntl_not_bound)
{
int ret;
ret = zsock_fcntl(test_sock, F_SETFL, 0);
zassert_equal(0, ret, "fcntl() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].ioctl_called,
"fcntl() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on shutdown() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_shutdown_not_bound)
{
int ret;
ret = zsock_shutdown(test_sock, ZSOCK_SHUT_RD);
zassert_equal(0, ret, "shutdown() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].shutdown_called,
"shutdown() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on bind() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_bind_not_bound)
{
int ret;
struct sockaddr_in addr = {
.sin_family = AF_INET
};
ret = zsock_bind(test_sock, (struct sockaddr *)&addr, sizeof(addr));
zassert_equal(0, ret, "bind() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].bind_called,
"bind() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on connect() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_connect_not_bound)
{
int ret;
struct sockaddr_in addr = test_peer_addr;
ret = zsock_connect(test_sock, (struct sockaddr *)&addr, sizeof(addr));
zassert_equal(0, ret, "connect() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].connect_called,
"connect() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on listen() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_listen_not_bound)
{
int ret;
ret = zsock_listen(test_sock, 1);
zassert_equal(0, ret, "listen() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].listen_called,
"listen() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on accept() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_accept_not_bound)
{
int ret;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
ret = zsock_accept(test_sock, (struct sockaddr *)&addr, &addrlen);
zassert_equal(0, ret, "accept() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].accept_called,
"accept() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on sendto() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_sendto_not_bound)
{
int ret;
uint8_t dummy_data = 0;
struct sockaddr_in addr = test_peer_addr;
ret = zsock_sendto(test_sock, &dummy_data, 1, 0,
(struct sockaddr *)&addr, sizeof(addr));
zassert_equal(1, ret, "sendto() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].sendto_called,
"sendto() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on recvfrom() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_recvfrom_not_bound)
{
int ret;
uint8_t dummy_data = 0;
ret = zsock_recvfrom(test_sock, &dummy_data, 1, 0, NULL, 0);
zassert_equal(0, ret, "recvfrom() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].recvfrom_called,
"recvfrom() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on getsockopt() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_getsockopt_not_bound)
{
int ret;
struct timeval optval = { 0 };
socklen_t optlen = sizeof(optval);
ret = zsock_getsockopt(test_sock, SOL_SOCKET, SO_RCVTIMEO,
&optval, &optlen);
zassert_equal(0, ret, "getsockopt() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].getsockopt_called,
"getsockopt() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on setsockopt() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_setsockopt_not_bound)
{
int ret;
struct timeval optval = { 0 };
ret = zsock_setsockopt(test_sock, SOL_SOCKET, SO_RCVTIMEO,
&optval, sizeof(optval));
zassert_equal(0, ret, "setsockopt() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].setsockopt_called,
"setsockopt() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on sendmsg() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_sendmsg_not_bound)
{
int ret;
struct msghdr dummy_msg = { 0 };
ret = zsock_sendmsg(test_sock, &dummy_msg, 0);
zassert_equal(0, ret, "sendmsg() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].sendmsg_called,
"sendmsg() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on getpeername() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_getpeername_not_bound)
{
int ret;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
ret = zsock_getpeername(test_sock, (struct sockaddr *)&addr, &addrlen);
zassert_equal(0, ret, "getpeername() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].getpeername_called,
"getpeername() should've been dispatched");
}
/* Verify that socket is automatically dispatched to a default socket
* implementation on getsockname() call, if not bound.
*/
ZTEST(net_socket_offload_udp, test_getsockname_not_bound)
{
int ret;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
ret = zsock_getsockname(test_sock, (struct sockaddr *)&addr, &addrlen);
zassert_equal(0, ret, "getsockname() failed");
zassert_true(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket should've been dispatched");
zassert_true(test_socket_ctx[OFFLOAD_1].getsockname_called,
"getsockname() should've been dispatched");
}
/* Verify that socket is dispatched to a proper offloaded socket implementation
* if the socket is bound to an offloaded interface.
*/
ZTEST(net_socket_offload_udp, test_so_bindtodevice_iface_offloaded)
{
int ret;
uint8_t dummy_data = 0;
struct ifreq ifreq = {
.ifr_name = "offloaded_2"
};
struct sockaddr_in addr = {
.sin_family = AF_INET
};
ret = zsock_setsockopt(test_sock, SOL_SOCKET, SO_BINDTODEVICE,
&ifreq, sizeof(ifreq));
zassert_equal(0, ret, "setsockopt() failed");
zassert_false(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket dispatched to wrong iface");
zassert_true(test_socket_ctx[OFFLOAD_2].socket_called,
"Socket should've been dispatched to offloaded iface 2");
zassert_true(test_socket_ctx[OFFLOAD_2].setsockopt_called,
"setsockopt() should've been dispatched");
ret = zsock_sendto(test_sock, &dummy_data, 1, 0,
(struct sockaddr *)&addr, sizeof(addr));
zassert_equal(1, ret, "sendto() failed");
zassert_true(test_socket_ctx[OFFLOAD_2].sendto_called,
"sendto() should've been dispatched");
}
/* Verify that socket is dispatched to a native socket implementation
* if the socket is bound to a native interface.
*/
ZTEST(net_socket_offload_udp, test_so_bindtodevice_iface_native)
{
int ret;
uint8_t dummy_data = 0;
struct ifreq ifreq = {
.ifr_name = "dummy_native"
};
struct sockaddr_in addr = test_peer_addr;
ret = zsock_setsockopt(test_sock, SOL_SOCKET, SO_BINDTODEVICE,
&ifreq, sizeof(ifreq));
zassert_equal(0, ret, "setsockopt() failed");
zassert_false(test_socket_ctx[OFFLOAD_1].socket_called,
"Socket dispatched to wrong iface");
zassert_false(test_socket_ctx[OFFLOAD_2].socket_called,
"Socket dispatched to wrong iface");
ret = zsock_sendto(test_sock, &dummy_data, 1, 0,
(struct sockaddr *)&addr, sizeof(addr));
zassert_equal(1, ret, "sendto() failed %d", errno);
ret = k_sem_take(&test_native_send_called, K_MSEC(200));
zassert_equal(0, ret, "sendto() should've been dispatched to native iface");
}
/* Verify that the underlying socket is dispatched to a proper offloaded socket
* implementation if native TLS is used and the socket is bound to an offloaded
* interface.
*/
ZTEST(net_socket_offload_tls, test_tls_native_iface_offloaded)
{
int ret;
const struct fd_op_vtable *vtable;
void *obj;
struct ifreq ifreq = {
.ifr_name = "offloaded_2"
};
int tls_native = 1;
struct sockaddr_in addr = test_peer_addr;
ret = zsock_setsockopt(test_sock, SOL_TLS, TLS_NATIVE,
&tls_native, sizeof(tls_native));
zassert_equal(0, ret, "setsockopt() failed");
zassert_false(test_socket_ctx[OFFLOAD_1].socket_called,
"TLS socket dispatched to wrong iface");
zassert_false(test_socket_ctx[OFFLOAD_2].socket_called,
"TLS socket dispatched to wrong iface");
obj = z_get_fd_obj_and_vtable(test_sock, &vtable, NULL);
zassert_not_null(obj, "No obj found");
zassert_true(net_socket_is_tls(obj), "Socket is not a native TLS sock");
ret = zsock_setsockopt(test_sock, SOL_SOCKET, SO_BINDTODEVICE,
&ifreq, sizeof(ifreq));
zassert_equal(0, ret, "setsockopt() failed");
zassert_false(test_socket_ctx[OFFLOAD_1].socket_called,
"Underlying socket dispatched to wrong iface");
zassert_true(test_socket_ctx[OFFLOAD_2].socket_called,
"Underlying socket dispatched to wrong iface");
/* Ignore connect result as it will fail anyway. Just verify the
* call/packets were forwarded to a valid iface.
*/
ret = zsock_connect(test_sock, (struct sockaddr *)&addr, sizeof(addr));
zassert_true(test_socket_ctx[OFFLOAD_2].connect_called,
"connect() should've been dispatched to offloaded_2 iface");
}
/* Verify that the underlying socket is dispatched to a native socket
* implementation if native TLS is used and the socket is bound to a native
* interface.
*/
ZTEST(net_socket_offload_tls, test_tls_native_iface_native)
{
int ret;
const struct fd_op_vtable *vtable;
void *obj;
struct ifreq ifreq = {
.ifr_name = "dummy_native"
};
int tls_native = 1;
struct sockaddr_in addr = test_peer_addr;
ret = zsock_setsockopt(test_sock, SOL_TLS, TLS_NATIVE,
&tls_native, sizeof(tls_native));
zassert_equal(0, ret, "setsockopt() failed");
zassert_false(test_socket_ctx[OFFLOAD_1].socket_called,
"TLS socket dispatched to wrong iface");
zassert_false(test_socket_ctx[OFFLOAD_2].socket_called,
"TLS socket dispatched to wrong iface");
obj = z_get_fd_obj_and_vtable(test_sock, &vtable, NULL);
zassert_not_null(obj, "No obj found");
zassert_true(net_socket_is_tls(obj), "Socket is not a native TLS sock");
ret = zsock_setsockopt(test_sock, SOL_SOCKET, SO_BINDTODEVICE,
&ifreq, sizeof(ifreq));
zassert_equal(0, ret, "setsockopt() failed");
zassert_false(test_socket_ctx[OFFLOAD_1].socket_called,
"Underlying socket dispatched to wrong iface");
zassert_false(test_socket_ctx[OFFLOAD_2].socket_called,
"Underlying socket dispatched to wrong iface");
/* Ignore connect result as it will fail anyway. Just verify the
* call/packets were forwarded to a valid iface.
*/
(void)zsock_connect(test_sock, (struct sockaddr *)&addr, sizeof(addr));
ret = k_sem_take(&test_native_send_called, K_MSEC(200));
zassert_equal(0, ret, "sendto() should've been dispatched to native iface");
}
ZTEST_SUITE(net_socket_offload_udp, NULL, NULL, test_socket_setup_udp,
test_socket_teardown, NULL);
ZTEST_SUITE(net_socket_offload_tls, NULL, NULL, test_socket_setup_tls,
test_socket_teardown, NULL);
ZTEST_SUITE(net_socket_offload_close, NULL, NULL, test_socket_setup_udp,
NULL, NULL);