net: sockets: IPv6_MULTICAST_LOOP Support

Add support for the IPV6_MULTICAST_LOOP socket option.
This option allows enabling or disabling the consumption of multicast
packets by the sender. By default, the socket option will be enabled.

Co-authored-by: Pieter De Gendt <pieter.degendt@gmail.com>

Signed-off-by: Sayooj K Karun <sayooj@aerlync.com>
This commit is contained in:
Sayooj K Karun 2025-03-17 16:58:55 +05:30 committed by Benjamin Cabé
commit 273d60164d
6 changed files with 140 additions and 0 deletions

View file

@ -7,6 +7,7 @@
/*
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2021 Nordic Semiconductor
* Copyright (c) 2025 Aerlync Labs Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -390,6 +391,10 @@ __net_socket struct net_context {
*/
uint8_t ipv4_mcast_ifindex;
};
/** Flag to enable/disable multicast loop */
union {
bool ipv6_mcast_loop; /**< IPv6 multicast loop */
};
#endif /* CONFIG_NET_IPV6 || CONFIG_NET_IPV4 */
#if defined(CONFIG_NET_CONTEXT_TIMESTAMPING)
@ -897,6 +902,40 @@ static inline void net_context_set_ipv6_mcast_hop_limit(struct net_context *cont
context->ipv6_mcast_hop_limit = hop_limit;
}
#if defined(CONFIG_NET_IPV6)
/**
* @brief Get IPv6 multicast loop value for this context.
*
* @details This function returns the IPv6 multicast loop value
* that is set to this context.
*
* @param context Network context.
*
* @return IPv6 multicast loop value
*/
static inline bool net_context_get_ipv6_mcast_loop(struct net_context *context)
{
return context->options.ipv6_mcast_loop;
}
/**
* @brief Set IPv6 multicast loop value for this context.
*
* @details This function sets the IPv6 multicast loop value for
* this context.
*
* @param context Network context.
* @param ipv6_mcast_loop IPv6 multicast loop value.
*/
static inline void net_context_set_ipv6_mcast_loop(struct net_context *context,
bool ipv6_mcast_loop)
{
context->options.ipv6_mcast_loop = ipv6_mcast_loop;
}
#endif
/**
* @brief Enable or disable socks proxy support for this context.
*
@ -1325,6 +1364,7 @@ enum net_context_option {
NET_OPT_MCAST_IFINDEX = 19, /**< IPv6 multicast output network interface index */
NET_OPT_MTU = 20, /**< IPv4 socket path MTU */
NET_OPT_LOCAL_PORT_RANGE = 21, /**< Clamp local port range */
NET_OPT_IPV6_MCAST_LOOP = 22, /**< IPV6 multicast loop */
};
/**

View file

@ -8,6 +8,7 @@
/*
* Copyright (c) 2017-2018 Linaro Limited
* Copyright (c) 2021 Nordic Semiconductor
* Copyright (c) 2025 Aerlync Labs Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -1005,6 +1006,9 @@ struct ip_mreq {
/** Set the multicast hop limit for the socket. */
#define IPV6_MULTICAST_HOPS 18
/** Set the multicast loop bit for the socket. */
#define IPV6_MULTICAST_LOOP 19
/** Join IPv6 multicast group. */
#define IPV6_ADD_MEMBERSHIP 20

View file

@ -1,6 +1,7 @@
# IPv6 Options
# Copyright (c) 2016 Intel Corporation.
# Copyright (c) 2025 Aerlync Labs Inc.
# SPDX-License-Identifier: Apache-2.0
menuconfig NET_IPV6
@ -81,6 +82,13 @@ config NET_INITIAL_MCAST_HOP_LIMIT
don't leave the local network unless the application explicitly
requests it.
config NET_INITIAL_IPV6_MCAST_LOOP
bool "Control whether the socket sees multicast packets sent by itself"
default y
help
Assign initial value to IPV6_MULTICAST_LOOP in socket options,
if not set by the user using setsockopt().
config NET_IPV6_MAX_NEIGHBORS
int "How many IPv6 neighbors are supported"
default 8

View file

@ -7,6 +7,7 @@
/*
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2021 Nordic Semiconductor
* Copyright (c) 2025 Aerlync Labs Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -583,6 +584,10 @@ int net_context_get(sa_family_t family, enum net_sock_type type, uint16_t proto,
contexts[i].ipv6_hop_limit = INITIAL_HOP_LIMIT;
contexts[i].ipv6_mcast_hop_limit = INITIAL_MCAST_HOP_LIMIT;
#if defined(CONFIG_NET_IPV6)
contexts[i].options.ipv6_mcast_loop =
IS_ENABLED(CONFIG_NET_INITIAL_IPV6_MCAST_LOOP);
#endif
}
if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) {
struct sockaddr_in *addr = (struct sockaddr_in *)&contexts[i].local;
@ -2033,6 +2038,20 @@ static int get_context_local_port_range(struct net_context *context,
#endif
}
static int get_context_ipv6_mcast_loop(struct net_context *context,
void *value, size_t *len)
{
#if defined(CONFIG_NET_IPV6)
return get_bool_option(context->options.ipv6_mcast_loop, value, len);
#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.
*/
@ -3329,6 +3348,20 @@ static int set_context_unicast_hop_limit(struct net_context *context,
#endif
}
static int set_context_ipv6_mcast_loop(struct net_context *context,
const void *value, size_t len)
{
#if defined(CONFIG_NET_IPV6)
return set_bool_option(&context->options.ipv6_mcast_loop, value, len);
#else
ARG_UNUSED(context);
ARG_UNUSED(value);
ARG_UNUSED(len);
return -ENOTSUP;
#endif
}
static int set_context_reuseaddr(struct net_context *context,
const void *value, size_t len)
{
@ -3652,6 +3685,9 @@ int net_context_set_option(struct net_context *context,
case NET_OPT_LOCAL_PORT_RANGE:
ret = set_context_local_port_range(context, value, len);
break;
case NET_OPT_IPV6_MCAST_LOOP:
ret = set_context_ipv6_mcast_loop(context, value, len);
break;
}
k_mutex_unlock(&context->lock);
@ -3737,6 +3773,9 @@ int net_context_get_option(struct net_context *context,
case NET_OPT_LOCAL_PORT_RANGE:
ret = get_context_local_port_range(context, value, len);
break;
case NET_OPT_IPV6_MCAST_LOOP:
ret = get_context_ipv6_mcast_loop(context, value, len);
break;
}
k_mutex_unlock(&context->lock);

View file

@ -7,6 +7,7 @@
/*
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2025 Aerlync Labs Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -409,6 +410,30 @@ int net_try_send_data(struct net_pkt *pkt, k_timeout_t timeout)
goto err;
}
#if defined(CONFIG_NET_IPV6)
if (net_pkt_family(pkt) == AF_INET6) {
const struct in6_addr *dest = (const struct in6_addr *)&NET_IPV6_HDR(pkt)->dst;
struct net_context *ctx = net_pkt_context(pkt);
if (net_ipv6_is_addr_mcast(dest) && ctx != NULL &&
net_context_get_ipv6_mcast_loop(ctx)) {
struct net_pkt *clone = net_pkt_clone(pkt, K_NO_WAIT);
if (clone != NULL) {
net_pkt_set_iface(clone, net_pkt_iface(pkt));
if (net_recv_data(net_pkt_iface(clone), clone) < 0) {
if (IS_ENABLED(CONFIG_NET_STATISTICS)) {
net_stats_update_ipv6_drop(net_pkt_iface(pkt));
}
net_pkt_unref(clone);
}
} else {
NET_DBG("Failed to clone multicast packet");
}
}
}
#endif
if (net_if_try_send_data(net_pkt_iface(pkt), pkt, timeout) == NET_DROP) {
ret = -EIO;
goto err;

View file

@ -2,6 +2,7 @@
* Copyright (c) 2017 Linaro Limited
* Copyright (c) 2021 Nordic Semiconductor
* Copyright (c) 2023 Arm Limited (or its affiliates). All rights reserved.
* Copyright (c) 2025 Aerlync Labs Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -2080,6 +2081,18 @@ int zsock_getsockopt_ctx(struct net_context *ctx, int level, int optname,
}
return 0;
case IPV6_MULTICAST_LOOP:
ret = net_context_get_option(ctx,
NET_OPT_IPV6_MCAST_LOOP,
optval, optlen);
if (ret < 0) {
errno = -ret;
return -1;
}
return 0;
}
break;
@ -2722,6 +2735,17 @@ int zsock_setsockopt_ctx(struct net_context *ctx, int level, int optname,
}
break;
case IPV6_MULTICAST_LOOP:
ret = net_context_set_option(ctx,
NET_OPT_IPV6_MCAST_LOOP,
optval, optlen);
if (ret < 0) {
errno = -ret;
return -1;
}
return 0;
}
break;