| /* |
| * Copyright (c) 2019 Linaro Limited |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); |
| |
| #include <stdio.h> |
| #include <zephyr/ztest_assert.h> |
| #include <zephyr/sys/sem.h> |
| |
| #include <zephyr/net/socket.h> |
| #include <zephyr/net/dummy.h> |
| |
| #include "../../socket_helpers.h" |
| |
| ZTEST_USER(socket_misc_test_suite, test_gethostname) |
| { |
| static ZTEST_BMEM char buf[80]; |
| int res; |
| |
| res = gethostname(buf, sizeof(buf)); |
| zassert_equal(res, 0, ""); |
| printk("%s\n", buf); |
| zassert_equal(strcmp(buf, "ztest_hostname"), 0, ""); |
| } |
| |
| ZTEST_USER(socket_misc_test_suite, test_inet_pton) |
| { |
| int res; |
| uint8_t buf[32]; |
| |
| res = inet_pton(AF_INET, "127.0.0.1", buf); |
| zassert_equal(res, 1, ""); |
| |
| res = inet_pton(AF_INET, "127.0.0.1a", buf); |
| zassert_equal(res, 0, ""); |
| |
| res = inet_pton(AF_INET6, "a:b:c:d:0:1:2:3", buf); |
| zassert_equal(res, 1, ""); |
| |
| res = inet_pton(AF_INET6, "::1", buf); |
| zassert_equal(res, 1, ""); |
| |
| res = inet_pton(AF_INET6, "1::", buf); |
| zassert_equal(res, 1, ""); |
| |
| res = inet_pton(AF_INET6, "a:b:c:d:0:1:2:3z", buf); |
| zassert_equal(res, 0, ""); |
| } |
| |
| #define TEST_MY_IPV4_ADDR "192.0.2.1" |
| #define TEST_PEER_IPV4_ADDR "192.0.2.2" |
| #define TEST_MY_IPV6_ADDR "2001:db8::1" |
| #define TEST_PEER_IPV6_ADDR "2001:db8::2" |
| |
| static struct in6_addr my_ipv6_addr1 = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; |
| static struct in_addr my_ipv4_addr1 = { { { 192, 0, 2, 1 } } }; |
| |
| static struct in6_addr my_ipv6_addr2 = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0x2 } } }; |
| static struct in_addr my_ipv4_addr2 = { { { 192, 0, 2, 2 } } }; |
| |
| static uint8_t lladdr1[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; |
| static uint8_t lladdr2[] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }; |
| |
| struct dummy_context { |
| struct net_if *iface; |
| uint8_t *mac_address; |
| struct in6_addr *ipv6_addr; |
| struct in_addr *ipv4_addr; |
| }; |
| |
| static struct dummy_context dummy_data1 = { |
| .mac_address = lladdr1, |
| .ipv6_addr = &my_ipv6_addr1, |
| .ipv4_addr = &my_ipv4_addr1 |
| }; |
| static struct dummy_context dummy_data2 = { |
| .mac_address = lladdr2, |
| .ipv6_addr = &my_ipv6_addr2, |
| .ipv4_addr = &my_ipv4_addr2 |
| }; |
| |
| static ZTEST_BMEM const struct device *current_dev; |
| static ZTEST_BMEM struct sys_sem send_sem; |
| |
| static int dummy_send(const struct device *dev, struct net_pkt *pkt) |
| { |
| struct net_pkt *recv_pkt; |
| int ret; |
| |
| ARG_UNUSED(dev); |
| ARG_UNUSED(pkt); |
| |
| NET_DBG("Sending data (%zd bytes) to iface %d\n", |
| net_pkt_get_len(pkt), net_if_get_by_iface(net_pkt_iface(pkt))); |
| |
| current_dev = dev; |
| |
| sys_sem_give(&send_sem); |
| |
| /* Report it back to the interface. */ |
| recv_pkt = net_pkt_clone(pkt, K_NO_WAIT); |
| if (recv_pkt == NULL) { |
| return -ENOMEM; |
| } |
| |
| ret = net_recv_data(net_pkt_iface(recv_pkt), recv_pkt); |
| zassert_equal(ret, 0, "Cannot receive data (%d)", ret); |
| |
| return 0; |
| } |
| |
| static void dummy_iface_init(struct net_if *iface) |
| { |
| const struct device *dev = net_if_get_device(iface); |
| struct dummy_context *ctx = dev->data; |
| struct net_if_addr *ifaddr; |
| |
| ctx->iface = iface; |
| |
| net_if_set_link_addr(iface, ctx->mac_address, 6, NET_LINK_DUMMY); |
| |
| ifaddr = net_if_ipv6_addr_add(net_if_lookup_by_dev(dev), ctx->ipv6_addr, |
| NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| zassert_not_null(ifaddr, "ipv6 addr"); |
| } |
| |
| ifaddr = net_if_ipv4_addr_add(net_if_lookup_by_dev(dev), ctx->ipv4_addr, |
| NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| zassert_not_null(ifaddr, "ipv4 addr"); |
| } |
| } |
| |
| static struct dummy_api dummy_api_funcs = { |
| .iface_api.init = dummy_iface_init, |
| .send = dummy_send, |
| }; |
| |
| static int dummy_init(const struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| |
| return 0; |
| } |
| |
| #define DEV1_NAME "dummy_1" |
| #define DEV2_NAME "dummy_2" |
| |
| NET_DEVICE_INIT(dummy_1, DEV1_NAME, dummy_init, |
| NULL, &dummy_data1, NULL, |
| CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &dummy_api_funcs, |
| DUMMY_L2, NET_L2_GET_CTX_TYPE(DUMMY_L2), 127); |
| |
| |
| NET_DEVICE_INIT(dummy_2, DEV2_NAME, dummy_init, |
| NULL, &dummy_data2, NULL, |
| CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &dummy_api_funcs, |
| DUMMY_L2, NET_L2_GET_CTX_TYPE(DUMMY_L2), 127); |
| |
| #define DST_PORT 4242 |
| #define BIND_PORT 4240 |
| |
| void test_so_bindtodevice(int sock_c, int sock_s, struct sockaddr *peer_addr, |
| socklen_t peer_addrlen, struct sockaddr *bind_addr, |
| socklen_t bind_addrlen) |
| { |
| int ret; |
| struct ifreq ifreq = { 0 }; |
| const struct device *dev1 = device_get_binding(DEV1_NAME); |
| const struct device *dev2 = device_get_binding(DEV2_NAME); |
| |
| uint8_t send_buf[32]; |
| uint8_t recv_buf[sizeof(send_buf)] = { 0 }; |
| |
| ret = bind(sock_s, bind_addr, bind_addrlen); |
| zassert_equal(ret, 0, "bind failed, %d", errno); |
| |
| /* Bind server socket with interface 2. */ |
| |
| strcpy(ifreq.ifr_name, DEV2_NAME); |
| ret = setsockopt(sock_s, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, |
| sizeof(ifreq)); |
| zassert_equal(ret, 0, "SO_BINDTODEVICE failed, %d", errno); |
| |
| /* Bind client socket with interface 1 and send a packet. */ |
| |
| current_dev = NULL; |
| sys_sem_init(&send_sem, 0, 1); |
| strcpy(ifreq.ifr_name, DEV1_NAME); |
| strcpy(send_buf, DEV1_NAME); |
| |
| ret = setsockopt(sock_c, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, |
| sizeof(ifreq)); |
| zassert_equal(ret, 0, "SO_BINDTODEVICE failed, %d", errno); |
| |
| ret = sendto(sock_c, send_buf, strlen(send_buf) + 1, 0, |
| peer_addr, peer_addrlen); |
| zassert_equal(ret, strlen(send_buf) + 1, "sendto failed, %d", errno); |
| |
| ret = sys_sem_take(&send_sem, K_MSEC(100)); |
| zassert_equal(ret, 0, "iface did not receive packet"); |
| |
| zassert_equal_ptr(dev1, current_dev, "invalid interface used (%p vs %p)", |
| dev1, current_dev); |
| |
| k_msleep(10); |
| |
| /* Bind client socket with interface 2 and send a packet. */ |
| |
| current_dev = NULL; |
| sys_sem_init(&send_sem, 0, 1); |
| strcpy(ifreq.ifr_name, DEV2_NAME); |
| strcpy(send_buf, DEV2_NAME); |
| |
| ret = setsockopt(sock_c, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, |
| sizeof(ifreq)); |
| zassert_equal(ret, 0, "SO_BINDTODEVICE failed, %d", errno); |
| |
| ret = sendto(sock_c, send_buf, strlen(send_buf) + 1, 0, |
| peer_addr, peer_addrlen); |
| zassert_equal(ret, strlen(send_buf) + 1, "sendto failed, %d", errno); |
| |
| ret = sys_sem_take(&send_sem, K_MSEC(100)); |
| zassert_equal(ret, 0, "iface did not receive packet"); |
| |
| zassert_equal_ptr(dev2, current_dev, "invalid interface used (%p vs %p)", |
| dev2, current_dev); |
| |
| /* Server socket should only receive data from the bound interface. */ |
| |
| k_msleep(10); |
| |
| ret = recv(sock_s, recv_buf, sizeof(recv_buf), MSG_DONTWAIT); |
| zassert_true(ret > 0, "recv failed, %d", errno); |
| zassert_mem_equal(recv_buf, DEV2_NAME, strlen(DEV2_NAME), |
| "received datagram from invalid interface"); |
| |
| /* Remove the binding from the server socket. */ |
| |
| strcpy(ifreq.ifr_name, ""); |
| ret = setsockopt(sock_s, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, |
| sizeof(ifreq)); |
| zassert_equal(ret, 0, "SO_BINDTODEVICE failed, %d", errno); |
| |
| /* Bind client socket with interface 1 again. */ |
| |
| sys_sem_init(&send_sem, 0, 1); |
| strcpy(ifreq.ifr_name, DEV1_NAME); |
| strcpy(send_buf, DEV1_NAME); |
| |
| ret = setsockopt(sock_c, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, |
| sizeof(ifreq)); |
| zassert_equal(ret, 0, "SO_BINDTODEVICE failed, %d", errno); |
| |
| ret = sendto(sock_c, send_buf, strlen(send_buf) + 1, 0, |
| peer_addr, peer_addrlen); |
| zassert_equal(ret, strlen(send_buf) + 1, "sendto failed, %d", errno); |
| |
| ret = sys_sem_take(&send_sem, K_MSEC(100)); |
| zassert_equal(ret, 0, "iface did not receive packet"); |
| |
| zassert_equal_ptr(dev1, current_dev, "invalid interface used"); |
| |
| /* Server socket should now receive data from interface 1 as well. */ |
| |
| k_msleep(10); |
| |
| ret = recv(sock_s, recv_buf, sizeof(recv_buf), MSG_DONTWAIT); |
| zassert_true(ret > 0, "recv failed, %d", errno); |
| zassert_mem_equal(recv_buf, DEV1_NAME, strlen(DEV1_NAME), |
| "received datagram from invalid interface"); |
| |
| ret = close(sock_c); |
| zassert_equal(ret, 0, "close failed, %d", errno); |
| ret = close(sock_s); |
| zassert_equal(ret, 0, "close failed, %d", errno); |
| } |
| |
| void test_ipv4_so_bindtodevice(void) |
| { |
| int ret; |
| int sock_c; |
| int sock_s; |
| struct sockaddr_in peer_addr; |
| struct sockaddr_in bind_addr = { |
| .sin_family = AF_INET, |
| .sin_port = htons(DST_PORT), |
| .sin_addr = INADDR_ANY_INIT, |
| }; |
| |
| sock_c = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
| zassert_true(sock_c >= 0, "socket open failed"); |
| sock_s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
| zassert_true(sock_s >= 0, "socket open failed"); |
| |
| peer_addr.sin_family = AF_INET; |
| peer_addr.sin_port = htons(DST_PORT); |
| ret = inet_pton(AF_INET, TEST_PEER_IPV4_ADDR, |
| &peer_addr.sin_addr); |
| zassert_equal(ret, 1, "inet_pton failed"); |
| |
| test_so_bindtodevice(sock_c, sock_s, (struct sockaddr *)&peer_addr, |
| sizeof(peer_addr), (struct sockaddr *)&bind_addr, |
| sizeof(bind_addr)); |
| } |
| |
| void test_ipv6_so_bindtodevice(void) |
| { |
| int ret; |
| int sock_c; |
| int sock_s; |
| struct sockaddr_in6 peer_addr; |
| struct sockaddr_in6 bind_addr = { |
| .sin6_family = AF_INET6, |
| .sin6_port = htons(DST_PORT), |
| .sin6_addr = IN6ADDR_ANY_INIT, |
| }; |
| |
| sock_c = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); |
| zassert_true(sock_c >= 0, "socket open failed"); |
| sock_s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); |
| zassert_true(sock_s >= 0, "socket open failed"); |
| |
| peer_addr.sin6_family = AF_INET6; |
| peer_addr.sin6_port = htons(DST_PORT); |
| ret = inet_pton(AF_INET6, TEST_PEER_IPV6_ADDR, |
| &peer_addr.sin6_addr); |
| zassert_equal(ret, 1, "inet_pton failed"); |
| |
| test_so_bindtodevice(sock_c, sock_s, (struct sockaddr *)&peer_addr, |
| sizeof(peer_addr), (struct sockaddr *)&bind_addr, |
| sizeof(bind_addr)); |
| } |
| |
| #define ADDR_SIZE(family) ((family == AF_INET) ? \ |
| sizeof(struct sockaddr_in) : \ |
| sizeof(struct sockaddr_in6)) |
| |
| void test_getpeername(int family) |
| { |
| int ret; |
| int sock_c; |
| int sock_s; |
| struct sockaddr peer_addr; |
| socklen_t peer_addr_len; |
| struct sockaddr srv_addr = { 0 }; |
| |
| srv_addr.sa_family = family; |
| if (family == AF_INET) { |
| net_sin(&srv_addr)->sin_port = htons(DST_PORT); |
| ret = inet_pton(AF_INET, TEST_MY_IPV4_ADDR, |
| &net_sin(&srv_addr)->sin_addr); |
| } else { |
| net_sin6(&srv_addr)->sin6_port = htons(DST_PORT); |
| ret = inet_pton(AF_INET6, TEST_MY_IPV6_ADDR, |
| &net_sin6(&srv_addr)->sin6_addr); |
| } |
| zassert_equal(ret, 1, "inet_pton failed"); |
| |
| /* UDP socket */ |
| sock_c = socket(family, SOCK_DGRAM, IPPROTO_UDP); |
| zassert_true(sock_c >= 0, "socket open failed"); |
| |
| peer_addr_len = ADDR_SIZE(family); |
| ret = getpeername(sock_c, &peer_addr, &peer_addr_len); |
| zassert_equal(ret, -1, "getpeername shouldn've failed"); |
| zassert_equal(errno, ENOTCONN, "getpeername returned invalid error"); |
| |
| ret = connect(sock_c, &srv_addr, ADDR_SIZE(family)); |
| zassert_equal(ret, 0, "connect failed"); |
| |
| memset(&peer_addr, 0, sizeof(peer_addr)); |
| peer_addr_len = ADDR_SIZE(family); |
| ret = getpeername(sock_c, &peer_addr, &peer_addr_len); |
| zassert_equal(ret, 0, "getpeername failed"); |
| zassert_mem_equal(&peer_addr, &srv_addr, ADDR_SIZE(family), |
| "obtained wrong address"); |
| |
| ret = close(sock_c); |
| zassert_equal(ret, 0, "close failed, %d", errno); |
| |
| /* TCP socket */ |
| sock_c = socket(family, SOCK_STREAM, IPPROTO_TCP); |
| zassert_true(sock_c >= 0, "socket open failed"); |
| sock_s = socket(family, SOCK_STREAM, IPPROTO_TCP); |
| zassert_true(sock_s >= 0, "socket open failed"); |
| |
| ret = bind(sock_s, &srv_addr, ADDR_SIZE(family)); |
| zassert_equal(ret, 0, "bind failed, %d", errno); |
| |
| ret = listen(sock_s, 1); |
| zassert_equal(ret, 0, "listen failed, %d", errno); |
| |
| peer_addr_len = ADDR_SIZE(family); |
| ret = getpeername(sock_c, &peer_addr, &peer_addr_len); |
| zassert_equal(ret, -1, "getpeername shouldn've failed"); |
| zassert_equal(errno, ENOTCONN, "getpeername returned invalid error"); |
| |
| ret = connect(sock_c, &srv_addr, ADDR_SIZE(family)); |
| zassert_equal(ret, 0, "connect failed"); |
| |
| memset(&peer_addr, 0, sizeof(peer_addr)); |
| peer_addr_len = ADDR_SIZE(family); |
| ret = getpeername(sock_c, &peer_addr, &peer_addr_len); |
| zassert_equal(ret, 0, "getpeername failed"); |
| zassert_mem_equal(&peer_addr, &srv_addr, ADDR_SIZE(family), |
| "obtained wrong address"); |
| |
| ret = close(sock_c); |
| zassert_equal(ret, 0, "close failed, %d", errno); |
| ret = close(sock_s); |
| zassert_equal(ret, 0, "close failed, %d", errno); |
| } |
| |
| |
| void test_ipv4_getpeername(void) |
| { |
| test_getpeername(AF_INET); |
| } |
| |
| void test_ipv6_getpeername(void) |
| { |
| test_getpeername(AF_INET6); |
| } |
| |
| static void *setup(void) |
| { |
| k_thread_system_pool_assign(k_current_get()); |
| return NULL; |
| } |
| |
| |
| ZTEST_USER(socket_misc_test_suite, test_ipv4) |
| { |
| test_ipv4_so_bindtodevice(); |
| test_ipv4_getpeername(); |
| } |
| |
| ZTEST_USER(socket_misc_test_suite, test_ipv6) |
| { |
| test_ipv6_so_bindtodevice(); |
| test_ipv6_getpeername(); |
| } |
| |
| ZTEST_SUITE(socket_misc_test_suite, NULL, setup, NULL, NULL, NULL); |