From 624f28cb65197018ff8d6b249c94d317b902ce14 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Fri, 15 Nov 2024 17:05:07 +0200 Subject: [PATCH] tests: net: socket: udp: Add IP_MULTICAST_IF set/get testing Add tests that verify that IP_MULTICAST_IF socket option set/get works as expected. Signed-off-by: Jukka Rissanen --- tests/net/socket/udp/src/main.c | 254 ++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) diff --git a/tests/net/socket/udp/src/main.c b/tests/net/socket/udp/src/main.c index 313111e2a7c..c98714e5f33 100644 --- a/tests/net/socket/udp/src/main.c +++ b/tests/net/socket/udp/src/main.c @@ -949,6 +949,7 @@ static struct in6_addr my_addr3 = { { { 0x20, 0x01, 0x0d, 0xb8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3 } } }; static struct in6_addr my_mcast_addr1 = { { { 0xff, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; +static struct in_addr my_mcast_addr2 = { { { 224, 0, 0, 2 } } }; static uint8_t server_lladdr[] = { 0x01, 0x02, 0x03, 0xff, 0xfe, 0x04, 0x05, 0x06 }; static struct net_linkaddr server_link_addr = { @@ -2671,6 +2672,259 @@ ZTEST(net_socket_udp, test_38_ipv6_multicast_ifindex) loopback_enable_address_swap(true); } +ZTEST(net_socket_udp, test_39_ipv4_multicast_ifindex) +{ + struct sockaddr_in saddr4 = { + .sin_family = AF_INET, + .sin_port = htons(SERVER_PORT), + .sin_addr = my_mcast_addr2, + }; + struct sockaddr_in dst_addr = { + .sin_family = AF_INET, + .sin_port = htons(SERVER_PORT), + .sin_addr = my_mcast_addr2, + }; + struct net_if_mcast_addr *ifmaddr; + struct net_if_addr *ifaddr; + struct in_addr addr = { 0 }; + struct ip_mreqn mreqn; + struct ip_mreq mreq; + struct net_if *iface; + int server_sock; + size_t addrlen; + size_t optlen; + int ifindex; + int sock; + int ret; + int err; + + net_if_foreach(iface_cb, ð_iface); + zassert_not_null(eth_iface, "No ethernet interface found"); + + ifmaddr = net_if_ipv4_maddr_add(eth_iface, &my_mcast_addr2); + if (!ifmaddr) { + DBG("Cannot add IPv4 multicast address %s\n", + net_sprint_ipv4_addr(&my_mcast_addr2)); + zassert_not_null(ifmaddr, "mcast_addr2"); + } + + ifaddr = net_if_ipv4_addr_add(eth_iface, &my_addr2, + NET_ADDR_MANUAL, 0); + if (!ifaddr) { + DBG("Cannot add IPv4 address %s\n", + net_sprint_ipv4_addr(&my_addr2)); + zassert_not_null(ifaddr, "addr2"); + } + + net_if_up(eth_iface); + + /* Check that we get the default interface */ + sock = zsock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + zassert_true(sock >= 0, "Cannot create socket (%d)", -errno); + + optlen = sizeof(addr); + ret = zsock_getsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &addr, &optlen); + zexpect_equal(ret, 0, "setsockopt failed (%d)", errno); + zexpect_equal(optlen, sizeof(addr), "invalid optlen %d vs %d", + optlen, sizeof(addr)); + ifindex = net_if_get_by_iface(net_if_get_default()); + ret = net_if_ipv4_addr_lookup_by_index(&addr); + zexpect_equal(ret, ifindex, + "getsockopt multicast ifindex (expected %d got %d)", + ifindex, ret); + + ret = zsock_close(sock); + zassert_equal(sock, 0, "Cannot close socket (%d)", -errno); + + /* Check failure for IPv6 socket */ + sock = zsock_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + zassert_true(sock >= 0, "Cannot create socket (%d)", -errno); + + optlen = 0U; + ret = zsock_getsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &addr, &optlen); + err = -errno; + zexpect_equal(ret, -1, "setsockopt failed (%d)", errno); + zexpect_equal(err, -EAFNOSUPPORT, "setsockopt failed (%d)", errno); + zexpect_equal(optlen, 0U, "setsockopt optlen (%d)", optlen); + + ret = zsock_close(sock); + zassert_equal(sock, 0, "Cannot close socket (%d)", -errno); + + /* Check that we can set the interface */ + sock = zsock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + zassert_true(sock >= 0, "Cannot create socket (%d)", -errno); + + /* Clear any existing interface value by setting it to 0 */ + optlen = sizeof(mreqn); + mreqn.imr_ifindex = 0; + mreqn.imr_address.s_addr = 0; + + ret = zsock_setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &mreqn, optlen); + err = -errno; + zexpect_equal(ret, 0, "setsockopt failed (%d)", err); + + /* Verify that we get the empty value */ + optlen = sizeof(addr); + ret = zsock_getsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &addr, &optlen); + err = -errno; + zexpect_equal(ret, 0, "setsockopt failed (%d)", err); + zexpect_equal(optlen, sizeof(addr), "setsockopt optlen (%d)", optlen); + + /* Set the output multicast packet interface to the default interface */ + optlen = sizeof(mreqn); + mreqn.imr_ifindex = net_if_get_by_iface(net_if_get_default()); + mreqn.imr_address.s_addr = 0; + + ret = zsock_setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &mreqn, optlen); + err = -errno; + zexpect_equal(ret, 0, "setsockopt failed (%d)", err); + + /* Verify that we get the default interface */ + optlen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + ret = zsock_getsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &addr, &optlen); + err = -errno; + zexpect_equal(ret, 0, "setsockopt failed (%d)", err); + zexpect_equal(optlen, sizeof(addr), "setsockopt optlen (%d)", optlen); + + ifaddr = net_if_ipv4_addr_lookup(&addr, &iface); + zexpect_not_null(ifaddr, "Address %s not found", + net_sprint_ipv4_addr(&addr)); + zexpect_equal(net_if_get_by_iface(iface), + net_if_get_by_iface(net_if_get_default()), + "Invalid interface %d vs %d", + net_if_get_by_iface(iface), + net_if_get_by_iface(net_if_get_default())); + + /* Now send a packet and verify that it is sent via the default + * interface instead of the Ethernet interface. + */ + server_sock = prepare_listen_sock_udp_v4(&saddr4); + zassert_not_equal(server_sock, -1, "Cannot create server socket (%d)", -errno); + + test_started = true; + loopback_enable_address_swap(false); + + ret = zsock_sendto(sock, TEST_STR_SMALL, sizeof(TEST_STR_SMALL) - 1, 0, + (struct sockaddr *)&dst_addr, sizeof(dst_addr)); + zexpect_equal(ret, STRLEN(TEST_STR_SMALL), + "invalid send len (was %d expected %d) (%d)", + ret, STRLEN(TEST_STR_SMALL), -errno); + + /* Test that the sent data is received from Ethernet interface. */ + addrlen = sizeof(saddr4); + ret = zsock_recvfrom(server_sock, rx_buf, sizeof(rx_buf), + 0, (struct sockaddr *)&saddr4, &addrlen); + zexpect_true(ret >= 0, "recvfrom fail"); + zexpect_equal(ret, strlen(TEST_STR_SMALL), + "unexpected received bytes"); + zexpect_mem_equal(rx_buf, TEST_STR_SMALL, sizeof(TEST_STR_SMALL) - 1, + "wrong data"); + + /* Clear the old interface value by setting it to 0 */ + optlen = sizeof(mreqn); + mreqn.imr_ifindex = 0; + mreqn.imr_address.s_addr = 0; + + ret = zsock_setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &mreqn, optlen); + err = -errno; + zexpect_equal(ret, 0, "setsockopt failed (%d)", err); + + /* Then do it the other way around, set the address but leave the + * interface number unassigned. + */ + optlen = sizeof(mreqn); + mreqn.imr_ifindex = 0; + + /* Get the address of default interface and set it as a target + * interface. + */ + ifaddr = net_if_ipv4_addr_get_first_by_index(net_if_get_by_iface(net_if_get_default())); + zexpect_not_null(ifaddr, "No address found for interface %d", + net_if_get_by_iface(net_if_get_default())); + mreqn.imr_address.s_addr = ifaddr->address.in_addr.s_addr; + + ret = zsock_setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &mreqn, optlen); + zexpect_equal(ret, 0, "setsockopt failed (%d)", errno); + + /* Verify that we get the default interface address */ + optlen = sizeof(struct in_addr); + ret = zsock_getsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &addr, &optlen); + err = -errno; + zexpect_equal(ret, 0, "setsockopt failed (%d)", err); + zexpect_equal(optlen, sizeof(struct in_addr), "setsockopt optlen (%d)", optlen); + ret = net_if_ipv4_addr_lookup_by_index(&addr); + zexpect_equal(ret, net_if_get_by_iface(net_if_get_default()), + "getsockopt multicast ifindex (expected %d got %d)", + net_if_get_by_iface(net_if_get_default()), ret); + zexpect_equal(ifaddr->address.in_addr.s_addr, + addr.s_addr, + "getsockopt iface address mismatch (expected %s got %s)", + net_sprint_ipv4_addr(&ifaddr->address.in_addr), + net_sprint_ipv4_addr(&addr)); + + ret = zsock_sendto(sock, TEST_STR_SMALL, sizeof(TEST_STR_SMALL) - 1, 0, + (struct sockaddr *)&dst_addr, sizeof(dst_addr)); + zexpect_equal(ret, STRLEN(TEST_STR_SMALL), + "invalid send len (was %d expected %d) (%d)", + ret, STRLEN(TEST_STR_SMALL), -errno); + + /* Test that the sent data is received from default interface. */ + addrlen = sizeof(saddr4); + ret = zsock_recvfrom(server_sock, rx_buf, sizeof(rx_buf), + 0, (struct sockaddr *)&saddr4, &addrlen); + zexpect_true(ret >= 0, "recvfrom fail"); + zexpect_equal(ret, strlen(TEST_STR_SMALL), + "unexpected received bytes"); + zexpect_mem_equal(rx_buf, TEST_STR_SMALL, sizeof(TEST_STR_SMALL) - 1, + "wrong data"); + + /* Then use mreq structure to set the interface */ + optlen = sizeof(mreq); + ifaddr = net_if_ipv4_addr_get_first_by_index(net_if_get_by_iface(net_if_get_default())); + zexpect_not_null(ifaddr, "No address found for interface %d", + net_if_get_by_iface(net_if_get_default())); + mreq.imr_interface.s_addr = ifaddr->address.in_addr.s_addr; + + ret = zsock_setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &mreq, optlen); + zexpect_equal(ret, 0, "setsockopt failed (%d)", errno); + + ret = zsock_sendto(sock, TEST_STR_SMALL, sizeof(TEST_STR_SMALL) - 1, 0, + (struct sockaddr *)&dst_addr, sizeof(dst_addr)); + zexpect_equal(ret, STRLEN(TEST_STR_SMALL), + "invalid send len (was %d expected %d) (%d)", + ret, STRLEN(TEST_STR_SMALL), -errno); + + /* Test that the sent data is received from default interface. */ + addrlen = sizeof(saddr4); + ret = zsock_recvfrom(server_sock, rx_buf, sizeof(rx_buf), + 0, (struct sockaddr *)&saddr4, &addrlen); + zexpect_true(ret >= 0, "recvfrom fail"); + zexpect_equal(ret, strlen(TEST_STR_SMALL), + "unexpected received bytes"); + zexpect_mem_equal(rx_buf, TEST_STR_SMALL, sizeof(TEST_STR_SMALL) - 1, + "wrong data"); + + ret = zsock_close(sock); + zassert_equal(ret, 0, "Cannot close socket (%d)", -errno); + + ret = zsock_close(server_sock); + zassert_equal(ret, 0, "Cannot close socket (%d)", -errno); + + test_started = false; + loopback_enable_address_swap(true); +} + static void after(void *arg) { ARG_UNUSED(arg);