diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 4340c07df2b30..7a47ab577d8c7 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -25,6 +25,16 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #include +/* Allow network tests to control the IP addresses swapping */ +#if defined(CONFIG_NET_TEST) +static bool loopback_dont_swap_addresses; + +void loopback_enable_address_swap(bool swap_addresses) +{ + loopback_dont_swap_addresses = !swap_addresses; +} +#endif /* CONFIG_NET_TEST */ + int loopback_dev_init(const struct device *dev) { ARG_UNUSED(dev); @@ -123,17 +133,22 @@ static int loopback_send(const struct device *dev, struct net_pkt *pkt) /* We need to swap the IP addresses because otherwise * the packet will be dropped. + * + * Some of the network tests require that addresses are not swapped so allow + * the test to control this remotely. */ - if (net_pkt_family(pkt) == AF_INET6) { - net_ipv6_addr_copy_raw(NET_IPV6_HDR(cloned)->src, - NET_IPV6_HDR(pkt)->dst); - net_ipv6_addr_copy_raw(NET_IPV6_HDR(cloned)->dst, - NET_IPV6_HDR(pkt)->src); - } else { - net_ipv4_addr_copy_raw(NET_IPV4_HDR(cloned)->src, - NET_IPV4_HDR(pkt)->dst); - net_ipv4_addr_copy_raw(NET_IPV4_HDR(cloned)->dst, - NET_IPV4_HDR(pkt)->src); + if (!COND_CODE_1(CONFIG_NET_TEST, (loopback_dont_swap_addresses), (false))) { + if (net_pkt_family(pkt) == AF_INET6) { + net_ipv6_addr_copy_raw(NET_IPV6_HDR(cloned)->src, + NET_IPV6_HDR(pkt)->dst); + net_ipv6_addr_copy_raw(NET_IPV6_HDR(cloned)->dst, + NET_IPV6_HDR(pkt)->src); + } else { + net_ipv4_addr_copy_raw(NET_IPV4_HDR(cloned)->src, + NET_IPV4_HDR(pkt)->dst); + net_ipv4_addr_copy_raw(NET_IPV4_HDR(cloned)->dst, + NET_IPV4_HDR(pkt)->src); + } } res = net_recv_data(net_pkt_iface(cloned), cloned); diff --git a/include/zephyr/net/net_context.h b/include/zephyr/net/net_context.h index 6b7e2a7e233e8..275496d91b20f 100644 --- a/include/zephyr/net/net_context.h +++ b/include/zephyr/net/net_context.h @@ -362,6 +362,22 @@ __net_socket struct net_context { */ uint16_t addr_preferences; #endif +#if defined(CONFIG_NET_IPV6) || defined(CONFIG_NET_IPV4) + union { + /** + * IPv6 multicast output network interface for this context/socket. + * Only allowed for SOCK_DGRAM or SOCK_RAW type sockets. + */ + uint8_t ipv6_mcast_ifindex; + + /** + * IPv4 multicast output network interface for this context/socket. + * Only allowed for SOCK_DGRAM type sockets. + */ + uint8_t ipv4_mcast_ifindex; + }; +#endif /* CONFIG_NET_IPV6 || CONFIG_NET_IPV4 */ + #if defined(CONFIG_NET_CONTEXT_TIMESTAMPING) /** Enable RX, TX or both timestamps of packets send through sockets. */ uint8_t timestamping; @@ -1292,6 +1308,7 @@ enum net_context_option { NET_OPT_TTL = 16, /**< IPv4 unicast TTL */ NET_OPT_ADDR_PREFERENCES = 17, /**< IPv6 address preference */ NET_OPT_TIMESTAMPING = 18, /**< Packet timestamping */ + NET_OPT_MCAST_IFINDEX = 19, /**< IPv6 multicast output network interface index */ NET_OPT_MTU = 20, /**< IPv4 socket path MTU */ }; diff --git a/include/zephyr/net/socket.h b/include/zephyr/net/socket.h index 06fd0be5ffacd..c5282fdde3792 100644 --- a/include/zephyr/net/socket.h +++ b/include/zephyr/net/socket.h @@ -1196,6 +1196,8 @@ struct in_pktinfo { */ #define IP_MTU 14 +/** Set IPv4 multicast datagram network interface. */ +#define IP_MULTICAST_IF 32 /** Set IPv4 multicast TTL value. */ #define IP_MULTICAST_TTL 33 /** Join IPv4 multicast group. */ @@ -1212,6 +1214,14 @@ struct ip_mreqn { int imr_ifindex; /**< Network interface index */ }; +/** + * @brief Struct used when setting a IPv4 multicast network interface. + */ +struct ip_mreq { + struct in_addr imr_multiaddr; /**< IP multicast group address */ + struct in_addr imr_interface; /**< IP address of local interface */ +}; + /** @} */ /** @@ -1222,6 +1232,9 @@ struct ip_mreqn { /** Set the unicast hop limit for the socket. */ #define IPV6_UNICAST_HOPS 16 +/** Set multicast output network interface index for the socket. */ +#define IPV6_MULTICAST_IF 17 + /** Set the multicast hop limit for the socket. */ #define IPV6_MULTICAST_HOPS 18 diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index 0d304e624eb1c..8ad8a2ad5a3ec 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -767,6 +767,17 @@ int net_context_bind(struct net_context *context, const struct sockaddr *addr, if (net_ipv6_is_addr_mcast(&addr6->sin6_addr)) { struct net_if_mcast_addr *maddr; + if (IS_ENABLED(CONFIG_NET_UDP) && + net_context_get_type(context) == SOCK_DGRAM) { + if (COND_CODE_1(CONFIG_NET_IPV6, + (context->options.ipv6_mcast_ifindex > 0), + (false))) { + IF_ENABLED(CONFIG_NET_IPV6, + (iface = net_if_get_by_index( + context->options.ipv6_mcast_ifindex))); + } + } + maddr = net_if_ipv6_maddr_lookup(&addr6->sin6_addr, &iface); if (!maddr) { @@ -869,6 +880,17 @@ int net_context_bind(struct net_context *context, const struct sockaddr *addr, if (net_ipv4_is_addr_mcast(&addr4->sin_addr)) { struct net_if_mcast_addr *maddr; + if (IS_ENABLED(CONFIG_NET_UDP) && + net_context_get_type(context) == SOCK_DGRAM) { + if (COND_CODE_1(CONFIG_NET_IPV4, + (context->options.ipv4_mcast_ifindex > 0), + (false))) { + IF_ENABLED(CONFIG_NET_IPV4, + (iface = net_if_get_by_index( + context->options.ipv4_mcast_ifindex))); + } + } + maddr = net_if_ipv4_maddr_lookup(&addr4->sin_addr, &iface); if (!maddr) { @@ -1302,7 +1324,13 @@ int net_context_connect(struct net_context *context, goto unlock; } - /* FIXME - Add multicast and broadcast address check */ + if (net_context_get_proto(context) == IPPROTO_TCP && + (net_ipv4_is_addr_mcast(&addr4->sin_addr) || + net_ipv4_is_addr_bcast(net_context_get_iface(context), + &addr4->sin_addr))) { + ret = -EADDRNOTAVAIL; + goto unlock; + } memcpy(&addr4->sin_addr, &net_sin(addr)->sin_addr, sizeof(struct in_addr)); @@ -1830,6 +1858,64 @@ static int get_context_mtu(struct net_context *context, return 0; } +static int get_context_mcast_ifindex(struct net_context *context, + void *value, size_t *len) +{ +#if defined(CONFIG_NET_IPV6) || defined(CONFIG_NET_IPV4) + sa_family_t family = net_context_get_family(context); + + if ((IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) || + (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET)) { + /* If user has not set the ifindex, then get the interface + * that this socket is bound to. + */ + if (context->options.ipv6_mcast_ifindex == 0) { + struct net_if *iface; + int ifindex; + + if (net_context_is_bound_to_iface(context)) { + iface = net_context_get_iface(context); + } else { + iface = net_if_get_default(); + } + + if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) { + if (!net_if_flag_is_set(iface, NET_IF_IPV6)) { + return -EPROTOTYPE; + } + } else if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) { + if (!net_if_flag_is_set(iface, NET_IF_IPV4)) { + return -EPROTOTYPE; + } + } + + ifindex = net_if_get_by_iface(iface); + if (ifindex < 1) { + return -ENOENT; + } + + *((int *)value) = ifindex; + } else { + *((int *)value) = context->options.ipv6_mcast_ifindex; + } + + if (len) { + *len = sizeof(int); + } + + return 0; + } + + return -EAFNOSUPPORT; +#else + ARG_UNUSED(context); + ARG_UNUSED(value); + ARG_UNUSED(len); + + return -ENOTSUP; +#endif +} + /* If buf is not NULL, then use it. Otherwise read the data to be written * to net_pkt from msghdr. */ @@ -2007,7 +2093,7 @@ static int context_sendto(struct net_context *context, bool sendto) { const struct msghdr *msghdr = NULL; - struct net_if *iface; + struct net_if *iface = NULL; struct net_pkt *pkt = NULL; sa_family_t family; size_t tmp_len; @@ -2067,6 +2153,17 @@ static int context_sendto(struct net_context *context, return -EDESTADDRREQ; } + if (IS_ENABLED(CONFIG_NET_UDP) && + net_context_get_type(context) == SOCK_DGRAM) { + if (net_ipv6_is_addr_mcast(&addr6->sin6_addr) && + COND_CODE_1(CONFIG_NET_IPV6, + (context->options.ipv6_mcast_ifindex > 0), (false))) { + IF_ENABLED(CONFIG_NET_IPV6, + (iface = net_if_get_by_index( + context->options.ipv6_mcast_ifindex))); + } + } + /* If application has not yet set the destination address * i.e., by not calling connect(), then set the interface * here so that the packet gets sent to the correct network @@ -2074,11 +2171,13 @@ static int context_sendto(struct net_context *context, * network interfaces and we are trying to send data to * second or later network interface. */ - if (net_ipv6_is_addr_unspecified( - &net_sin6(&context->remote)->sin6_addr) && - !net_context_is_bound_to_iface(context)) { - iface = net_if_ipv6_select_src_iface(&addr6->sin6_addr); - net_context_set_iface(context, iface); + if (iface == NULL) { + if (net_ipv6_is_addr_unspecified( + &net_sin6(&context->remote)->sin6_addr) && + !net_context_is_bound_to_iface(context)) { + iface = net_if_ipv6_select_src_iface(&addr6->sin6_addr); + net_context_set_iface(context, iface); + } } } else if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) { @@ -2120,6 +2219,17 @@ static int context_sendto(struct net_context *context, return -EDESTADDRREQ; } + if (IS_ENABLED(CONFIG_NET_UDP) && + net_context_get_type(context) == SOCK_DGRAM) { + if (net_ipv4_is_addr_mcast(&addr4->sin_addr) && + COND_CODE_1(CONFIG_NET_IPV4, + (context->options.ipv4_mcast_ifindex > 0), (false))) { + IF_ENABLED(CONFIG_NET_IPV4, + (iface = net_if_get_by_index( + context->options.ipv4_mcast_ifindex))); + } + } + /* If application has not yet set the destination address * i.e., by not calling connect(), then set the interface * here so that the packet gets sent to the correct network @@ -2127,10 +2237,12 @@ static int context_sendto(struct net_context *context, * network interfaces and we are trying to send data to * second or later network interface. */ - if (net_sin(&context->remote)->sin_addr.s_addr == 0U && - !net_context_is_bound_to_iface(context)) { - iface = net_if_ipv4_select_src_iface(&addr4->sin_addr); - net_context_set_iface(context, iface); + if (iface == NULL) { + if (net_sin(&context->remote)->sin_addr.s_addr == 0U && + !net_context_is_bound_to_iface(context)) { + iface = net_if_ipv4_select_src_iface(&addr4->sin_addr); + net_context_set_iface(context, iface); + } } } else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) && family == AF_PACKET) { @@ -3211,6 +3323,67 @@ static int set_context_timestamping(struct net_context *context, #endif } +static int set_context_mcast_ifindex(struct net_context *context, + const void *value, size_t len) +{ +#if defined(CONFIG_NET_IPV6) || defined(CONFIG_NET_IPV4) + sa_family_t family = net_context_get_family(context); + int mcast_ifindex = *((int *)value); + enum net_sock_type type; + struct net_if *iface; + + if ((IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) || + (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET)) { + + if (len != sizeof(int)) { + return -EINVAL; + } + + type = net_context_get_type(context); + if (type != SOCK_DGRAM) { + return -EINVAL; + } + + /* optlen equal to 0 then remove the binding */ + if (mcast_ifindex == 0) { + context->options.ipv6_mcast_ifindex = 0; + return 0; + } + + if (mcast_ifindex < 1 || mcast_ifindex > 255) { + return -EINVAL; + } + + iface = net_if_get_by_index(mcast_ifindex); + if (iface == NULL) { + return -ENOENT; + } + + if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) { + if (!net_if_flag_is_set(iface, NET_IF_IPV6)) { + return -EPROTOTYPE; + } + } else if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) { + if (!net_if_flag_is_set(iface, NET_IF_IPV4)) { + return -EPROTOTYPE; + } + } + + context->options.ipv6_mcast_ifindex = mcast_ifindex; + + return 0; + } + + return -EAFNOSUPPORT; +#else + ARG_UNUSED(context); + ARG_UNUSED(value); + ARG_UNUSED(len); + + return -ENOTSUP; +#endif +} + int net_context_set_option(struct net_context *context, enum net_context_option option, const void *value, size_t len) @@ -3290,6 +3463,9 @@ int net_context_set_option(struct net_context *context, ret = set_context_ipv6_mtu(context, value, len); } + break; + case NET_OPT_MCAST_IFINDEX: + ret = set_context_mcast_ifindex(context, value, len); break; } @@ -3370,6 +3546,9 @@ int net_context_get_option(struct net_context *context, case NET_OPT_MTU: ret = get_context_mtu(context, value, len); break; + case NET_OPT_MCAST_IFINDEX: + ret = get_context_mcast_ifindex(context, value, len); + break; } k_mutex_unlock(&context->lock); diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index 4033fe99ceade..002bae2b3a86d 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -3538,22 +3538,17 @@ bool net_if_ipv4_is_addr_bcast(struct net_if *iface, struct net_if *net_if_ipv4_select_src_iface(const struct in_addr *dst) { struct net_if *selected = NULL; + const struct in_addr *src; - STRUCT_SECTION_FOREACH(net_if, iface) { - bool ret; - - ret = net_if_ipv4_addr_mask_cmp(iface, dst); - if (ret) { - selected = iface; - goto out; - } + src = net_if_ipv4_select_src_addr(NULL, dst); + if (src != net_ipv4_unspecified_address()) { + net_if_ipv4_addr_lookup(src, &selected); } if (selected == NULL) { selected = net_if_get_default(); } -out: return selected; } @@ -3733,6 +3728,40 @@ const struct in_addr *net_if_ipv4_select_src_addr(struct net_if *dst_iface, return src; } +/* Internal function to get the first IPv4 address of the interface */ +struct net_if_addr *net_if_ipv4_addr_get_first_by_index(int ifindex) +{ + struct net_if *iface = net_if_get_by_index(ifindex); + struct net_if_addr *ifaddr = NULL; + struct net_if_ipv4 *ipv4; + + if (!iface) { + return NULL; + } + + net_if_lock(iface); + + ipv4 = iface->config.ip.ipv4; + if (!ipv4) { + goto out; + } + + ARRAY_FOR_EACH(ipv4->unicast, i) { + if (!ipv4->unicast[i].ipv4.is_used || + ipv4->unicast[i].ipv4.address.family != AF_INET) { + continue; + } + + ifaddr = &ipv4->unicast[i].ipv4; + break; + } + +out: + net_if_unlock(iface); + + return ifaddr; +} + struct net_if_addr *net_if_ipv4_addr_lookup(const struct in_addr *addr, struct net_if **ret) { diff --git a/subsys/net/ip/net_private.h b/subsys/net/ip/net_private.h index 9e3878c1d8e24..6bb626a5cbcd8 100644 --- a/subsys/net/ip/net_private.h +++ b/subsys/net/ip/net_private.h @@ -62,6 +62,8 @@ extern void net_if_stats_reset_all(void); extern void net_process_rx_packet(struct net_pkt *pkt); extern void net_process_tx_packet(struct net_pkt *pkt); +extern struct net_if_addr *net_if_ipv4_addr_get_first_by_index(int ifindex); + extern int net_icmp_call_ipv4_handlers(struct net_pkt *pkt, struct net_ipv4_hdr *ipv4_hdr, struct net_icmp_hdr *icmp_hdr); @@ -142,6 +144,10 @@ extern void mdns_init_responder(void); static inline void mdns_init_responder(void) { } #endif /* CONFIG_MDNS_RESPONDER */ +#if defined(CONFIG_NET_TEST) +extern void loopback_enable_address_swap(bool swap_addresses); +#endif /* CONFIG_NET_TEST */ + #if defined(CONFIG_NET_NATIVE) enum net_verdict net_ipv4_input(struct net_pkt *pkt, bool is_loopback); enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback); diff --git a/subsys/net/lib/sockets/sockets_inet.c b/subsys/net/lib/sockets/sockets_inet.c index 8e5673208aa3e..e88a6daa7f807 100644 --- a/subsys/net/lib/sockets/sockets_inet.c +++ b/subsys/net/lib/sockets/sockets_inet.c @@ -1612,6 +1612,99 @@ static enum tcp_conn_option get_tcp_option(int optname) return -EINVAL; } +static int ipv4_multicast_if(struct net_context *ctx, const void *optval, + socklen_t optlen, bool do_get) +{ + struct net_if *iface = NULL; + int ifindex, ret; + + if (do_get) { + struct net_if_addr *ifaddr; + size_t len = sizeof(ifindex); + + if (optval == NULL || (optlen != sizeof(struct in_addr))) { + errno = EINVAL; + return -1; + } + + ret = net_context_get_option(ctx, NET_OPT_MCAST_IFINDEX, + &ifindex, &len); + if (ret < 0) { + errno = -ret; + return -1; + } + + if (ifindex == 0) { + /* No interface set */ + ((struct in_addr *)optval)->s_addr = INADDR_ANY; + return 0; + } + + ifaddr = net_if_ipv4_addr_get_first_by_index(ifindex); + if (ifaddr == NULL) { + errno = ENOENT; + return -1; + } + + net_ipaddr_copy((struct in_addr *)optval, &ifaddr->address.in_addr); + + return 0; + } + + /* setsockopt() can accept either struct ip_mreqn or + * struct ip_mreq. We need to handle both cases. + */ + if (optval == NULL || (optlen != sizeof(struct ip_mreqn) && + optlen != sizeof(struct ip_mreq))) { + errno = EINVAL; + return -1; + } + + if (optlen == sizeof(struct ip_mreqn)) { + struct ip_mreqn *mreqn = (struct ip_mreqn *)optval; + + if (mreqn->imr_ifindex != 0) { + iface = net_if_get_by_index(mreqn->imr_ifindex); + + } else if (mreqn->imr_address.s_addr != INADDR_ANY) { + struct net_if_addr *ifaddr; + + ifaddr = net_if_ipv4_addr_lookup(&mreqn->imr_address, &iface); + if (ifaddr == NULL) { + errno = ENOENT; + return -1; + } + } + } else { + struct ip_mreq *mreq = (struct ip_mreq *)optval; + + if (mreq->imr_interface.s_addr != INADDR_ANY) { + struct net_if_addr *ifaddr; + + ifaddr = net_if_ipv4_addr_lookup(&mreq->imr_interface, &iface); + if (ifaddr == NULL) { + errno = ENOENT; + return -1; + } + } + } + + if (iface == NULL) { + ifindex = 0; + } else { + ifindex = net_if_get_by_iface(iface); + } + + ret = net_context_set_option(ctx, NET_OPT_MCAST_IFINDEX, + &ifindex, sizeof(ifindex)); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; +} + int zsock_getsockopt_ctx(struct net_context *ctx, int level, int optname, void *optval, socklen_t *optlen) { @@ -1831,6 +1924,18 @@ int zsock_getsockopt_ctx(struct net_context *ctx, int level, int optname, return 0; + case IP_MULTICAST_IF: + if (IS_ENABLED(CONFIG_NET_IPV4)) { + if (net_context_get_family(ctx) != AF_INET) { + errno = EAFNOSUPPORT; + return -1; + } + + return ipv4_multicast_if(ctx, optval, *optlen, true); + } + + break; + case IP_MULTICAST_TTL: ret = net_context_get_option(ctx, NET_OPT_MCAST_TTL, optval, optlen); @@ -1931,6 +2036,24 @@ int zsock_getsockopt_ctx(struct net_context *ctx, int level, int optname, return 0; + case IPV6_MULTICAST_IF: + if (IS_ENABLED(CONFIG_NET_IPV6)) { + if (net_context_get_family(ctx) != AF_INET6) { + errno = EAFNOSUPPORT; + return -1; + } + + ret = net_context_get_option(ctx, + NET_OPT_MCAST_IFINDEX, + optval, optlen); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; + } + case IPV6_MULTICAST_HOPS: ret = net_context_get_option(ctx, NET_OPT_MCAST_HOP_LIMIT, @@ -2395,6 +2518,13 @@ int zsock_setsockopt_ctx(struct net_context *ctx, int level, int optname, break; + case IP_MULTICAST_IF: + if (IS_ENABLED(CONFIG_NET_IPV4)) { + return ipv4_multicast_if(ctx, optval, optlen, false); + } + + break; + case IP_MULTICAST_TTL: ret = net_context_set_option(ctx, NET_OPT_MCAST_TTL, optval, optlen); @@ -2524,6 +2654,17 @@ int zsock_setsockopt_ctx(struct net_context *ctx, int level, int optname, return 0; + case IPV6_MULTICAST_IF: + ret = net_context_set_option(ctx, + NET_OPT_MCAST_IFINDEX, + optval, optlen); + if (ret < 0) { + errno = -ret; + return -1; + } + + return 0; + case IPV6_MULTICAST_HOPS: ret = net_context_set_option(ctx, NET_OPT_MCAST_HOP_LIMIT, diff --git a/tests/net/socket/udp/src/main.c b/tests/net/socket/udp/src/main.c index 5874ed9f6e4d9..c98714e5f3365 100644 --- a/tests/net/socket/udp/src/main.c +++ b/tests/net/socket/udp/src/main.c @@ -47,7 +47,7 @@ static const char test_str_all_tx_bufs[] = #define MY_IPV4_ADDR "127.0.0.1" #define MY_IPV6_ADDR "::1" #define MY_MCAST_IPV4_ADDR "224.0.0.1" -#define MY_MCAST_IPV6_ADDR "ff00::1" +#define MY_MCAST_IPV6_ADDR "ff01::1" #define ANY_PORT 0 #define SERVER_PORT 4242 @@ -947,6 +947,9 @@ static struct in6_addr my_addr1 = { { { 0x20, 0x01, 0x0d, 0xb8, 1, 0, 0, 0, static struct in_addr my_addr2 = { { { 192, 0, 2, 2 } } }; 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 = { @@ -957,6 +960,7 @@ static struct net_linkaddr server_link_addr = { #define PEER_IPV6_ADDR_ETH "2001:db8:100::2" #define TEST_TXTIME INT64_MAX #define WAIT_TIME K_MSEC(250) +#define WAIT_TIME_LONG K_MSEC(1000) static void eth_fake_iface_init(struct net_if *iface) { @@ -1015,6 +1019,7 @@ static void iface_cb(struct net_if *iface, void *user_data) if (net_if_l2(iface) == &NET_L2_GET_NAME(DUMMY)) { lo0 = iface; + net_if_set_default(iface); } } @@ -2531,6 +2536,395 @@ ZTEST(net_socket_udp, test_37_ipv6_src_addr_select) &my_addr3, &dest); } +ZTEST(net_socket_udp, test_38_ipv6_multicast_ifindex) +{ + struct sockaddr_in6 saddr6 = { + .sin6_family = AF_INET6, + .sin6_port = htons(SERVER_PORT), + .sin6_addr = my_mcast_addr1, + }; + struct net_if_mcast_addr *ifmaddr; + struct net_if_addr *ifaddr; + int server_sock; + size_t addrlen; + size_t optlen; + int ifindex; + int optval; + int sock; + int ret; + int err; + + net_if_foreach(iface_cb, ð_iface); + zassert_not_null(eth_iface, "No ethernet interface found"); + + ifmaddr = net_if_ipv6_maddr_add(eth_iface, &my_mcast_addr1); + if (!ifmaddr) { + DBG("Cannot add IPv6 multicast address %s\n", + net_sprint_ipv6_addr(&my_mcast_addr1)); + zassert_not_null(ifmaddr, "mcast_addr1"); + } + + ifaddr = net_if_ipv6_addr_add(eth_iface, &my_addr3, + NET_ADDR_AUTOCONF, 0); + if (!ifaddr) { + DBG("Cannot add IPv6 address %s\n", + net_sprint_ipv6_addr(&my_addr3)); + zassert_not_null(ifaddr, "addr1"); + } + + net_if_up(eth_iface); + + /* Check that we get the default interface */ + sock = zsock_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + zassert_true(sock >= 0, "Cannot create socket (%d)", -errno); + + optval = 0; optlen = 0U; + ret = zsock_getsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &optval, &optlen); + zexpect_equal(ret, 0, "setsockopt failed (%d)", errno); + zexpect_equal(optlen, sizeof(optval), "invalid optlen %d vs %d", + optlen, sizeof(optval)); + ifindex = net_if_get_by_iface(net_if_get_default()); + zexpect_equal(optval, ifindex, + "getsockopt multicast ifindex (expected %d got %d)", + ifindex, optval); + + ret = zsock_close(sock); + zassert_equal(sock, 0, "Cannot close socket (%d)", -errno); + + /* Check failure for IPv4 socket */ + sock = zsock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + zassert_true(sock >= 0, "Cannot create socket (%d)", -errno); + + optval = 0; optlen = 0U; + ret = zsock_getsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &optval, &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_INET6, SOCK_DGRAM, IPPROTO_UDP); + zassert_true(sock >= 0, "Cannot create socket (%d)", -errno); + + /* Clear any existing interface value by setting it to 0 */ + optval = 0; optlen = sizeof(int); + ret = zsock_setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &optval, optlen); + err = -errno; + zexpect_equal(ret, 0, "setsockopt failed (%d)", err); + zexpect_equal(optlen, sizeof(optval), "invalid optlen %d vs %d", + optlen, sizeof(optval)); + + /* Set the output multicast packet interface to the default interface */ + optval = net_if_get_by_iface(net_if_get_default()); optlen = sizeof(int); + ret = zsock_setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &optval, optlen); + zexpect_equal(ret, 0, "setsockopt failed (%d)", errno); + zexpect_equal(optlen, sizeof(optval), "invalid optlen %d vs %d", + optlen, sizeof(optval)); + + optval = 0; optlen = 0U; + ret = zsock_getsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &optval, &optlen); + err = -errno; + zexpect_equal(ret, 0, "setsockopt failed (%d)", err); + zexpect_equal(optlen, sizeof(int), "setsockopt optlen (%d)", optlen); + zexpect_equal(optval, 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()), optval); + + server_sock = prepare_listen_sock_udp_v6(&saddr6); + 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 *)&saddr6, sizeof(saddr6)); + 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 and + * not the Ethernet one. + */ + addrlen = sizeof(saddr6); + ret = zsock_recvfrom(server_sock, rx_buf, sizeof(rx_buf), + 0, (struct sockaddr *)&saddr6, &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); +} + +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);