net: context: Add support for adjusting IPv4 multicast ttl

Add option support for adjusting the IPv4 multicast
time-to-live value.

Fixes #60299

Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
This commit is contained in:
Jukka Rissanen 2023-11-27 17:23:15 +02:00 committed by Henrik Brix Andersen
parent a65ebbd6bc
commit de0268def0
12 changed files with 205 additions and 21 deletions

View file

@ -349,7 +349,10 @@ __net_socket struct net_context {
/** IPv6 hop limit or IPv4 ttl for packets sent via this context. */
union {
uint8_t ipv6_hop_limit;
uint8_t ipv4_ttl;
struct {
uint8_t ipv4_ttl;
uint8_t ipv4_mcast_ttl;
};
};
#if defined(CONFIG_SOCKS)
@ -703,6 +706,17 @@ static inline void net_context_set_ipv4_ttl(struct net_context *context,
context->ipv4_ttl = ttl;
}
static inline uint8_t net_context_get_ipv4_mcast_ttl(struct net_context *context)
{
return context->ipv4_mcast_ttl;
}
static inline void net_context_set_ipv4_mcast_ttl(struct net_context *context,
uint8_t ttl)
{
context->ipv4_mcast_ttl = ttl;
}
static inline uint8_t net_context_get_ipv6_hop_limit(struct net_context *context)
{
return context->ipv6_hop_limit;
@ -1106,6 +1120,7 @@ enum net_context_option {
NET_OPT_REUSEPORT = 10,
NET_OPT_IPV6_V6ONLY = 11,
NET_OPT_RECV_PKTINFO = 12,
NET_OPT_MCAST_TTL = 13,
};
/**

View file

@ -380,6 +380,9 @@ struct net_if_ipv4 {
/** IPv4 time-to-live */
uint8_t ttl;
/** IPv4 time-to-live for multicast packets */
uint8_t mcast_ttl;
};
#if defined(CONFIG_NET_DHCPV4) && defined(CONFIG_NET_NATIVE_IPV4)
@ -1916,6 +1919,23 @@ uint8_t net_if_ipv4_get_ttl(struct net_if *iface);
*/
void net_if_ipv4_set_ttl(struct net_if *iface, uint8_t ttl);
/**
* @brief Get IPv4 multicast time-to-live value specified for a given interface
*
* @param iface Network interface
*
* @return Time-to-live
*/
uint8_t net_if_ipv4_get_mcast_ttl(struct net_if *iface);
/**
* @brief Set IPv4 multicast time-to-live value specified to a given interface
*
* @param iface Network interface
* @param ttl Time-to-live value
*/
void net_if_ipv4_set_mcast_ttl(struct net_if *iface, uint8_t ttl);
/**
* @brief Check if this IPv4 address belongs to one of the interfaces.
*

View file

@ -1112,6 +1112,9 @@ struct in_pktinfo {
struct in_addr ipi_addr; /* Header Destination address */
};
/** sockopt: Set IPv4 multicast TTL value. */
#define IP_MULTICAST_TTL 33
/* Socket options for IPPROTO_IPV6 level */
/** sockopt: Don't support IPv4 access */
#define IPV6_V6ONLY 26

View file

@ -12,10 +12,27 @@ menuconfig NET_IPV4
if NET_IPV4
config NET_INITIAL_TTL
int "Initial time to live for a connection"
int "Initial IPv4 time to live value for unicast packets"
default 64
range 0 255
help
The value should be > 0
The value should normally be > 0. The device receiving the IPv4
packet will decrement the value and will drop the packet if the TTL
value is 0. When sending, the packet is dropped before transmitting
to network if TTL is 0.
config NET_INITIAL_MCAST_TTL
int "Initial IPv4 time to live value for multicast packets"
default 1
range 0 255
help
The value should normally be > 0. The device receiving the IPv4
packet will decrement the value and will drop the packet if the TTL
value is 0. When sending, the packet is dropped before transmitting
to network if TTL is 0.
The default is 1 (same as in Linux) which means that multicast packets
don't leave the local network unless the application explicitly
requests it.
config NET_IF_MAX_IPV4_COUNT
int "Max number of IPv4 network interfaces in the system"

View file

@ -191,6 +191,9 @@ static int igmp_v2_create_packet(struct net_pkt *pkt, const struct in_addr *dst,
const uint32_t router_alert = 0x94040000; /* RFC 2213 ch 2.1 */
int ret;
/* TTL set to 1, RFC 3376 ch 2 */
net_pkt_set_ipv4_ttl(pkt, 1U);
ret = net_ipv4_create_full(pkt,
net_if_ipv4_select_src_addr(
net_pkt_iface(pkt),
@ -199,8 +202,7 @@ static int igmp_v2_create_packet(struct net_pkt *pkt, const struct in_addr *dst,
0U,
0U,
0U,
0U,
1U); /* TTL set to 1, RFC 3376 ch 2 */
0U);
if (ret) {
return -ENOBUFS;
}
@ -222,8 +224,11 @@ static int igmp_v3_create_packet(struct net_pkt *pkt, const struct in_addr *dst,
const uint32_t router_alert = 0x94040000; /* RFC 2213 ch 2.1 */
int ret;
/* TTL set to 1, RFC 3376 ch 2 */
net_pkt_set_ipv4_ttl(pkt, 1U);
ret = net_ipv4_create_full(pkt, net_if_ipv4_select_src_addr(net_pkt_iface(pkt), dst), dst,
0U, 0U, 0U, 0U, 1U); /* TTL set to 1, RFC 3376 ch 2 */
0U, 0U, 0U, 0U);
if (ret) {
return -ENOBUFS;
}

View file

@ -37,8 +37,7 @@ int net_ipv4_create_full(struct net_pkt *pkt,
uint8_t tos,
uint16_t id,
uint8_t flags,
uint16_t offset,
uint8_t ttl)
uint16_t offset)
{
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv4_access, struct net_ipv4_hdr);
struct net_ipv4_hdr *ipv4_hdr;
@ -55,10 +54,24 @@ int net_ipv4_create_full(struct net_pkt *pkt,
ipv4_hdr->id[1] = id;
ipv4_hdr->offset[0] = (offset >> 8) | (flags << 5);
ipv4_hdr->offset[1] = offset;
ipv4_hdr->ttl = ttl;
if (ttl == 0U) {
ipv4_hdr->ttl = net_if_ipv4_get_ttl(net_pkt_iface(pkt));
ipv4_hdr->ttl = net_pkt_ipv4_ttl(pkt);
if (ipv4_hdr->ttl == 0U) {
if (net_ipv4_is_addr_mcast(dst)) {
if (net_pkt_context(pkt) != NULL) {
ipv4_hdr->ttl =
net_context_get_ipv4_mcast_ttl(net_pkt_context(pkt));
} else {
ipv4_hdr->ttl = net_if_ipv4_get_mcast_ttl(net_pkt_iface(pkt));
}
} else {
if (net_pkt_context(pkt) != NULL) {
ipv4_hdr->ttl =
net_context_get_ipv4_ttl(net_pkt_context(pkt));
} else {
ipv4_hdr->ttl = net_if_ipv4_get_ttl(net_pkt_iface(pkt));
}
}
}
ipv4_hdr->proto = 0U;
@ -83,8 +96,7 @@ int net_ipv4_create(struct net_pkt *pkt,
net_ipv4_set_ecn(&tos, net_pkt_ip_ecn(pkt));
}
return net_ipv4_create_full(pkt, src, dst, tos, 0U, 0U, 0U,
net_pkt_ipv4_ttl(pkt));
return net_ipv4_create_full(pkt, src, dst, tos, 0U, 0U, 0U);
}
int net_ipv4_finalize(struct net_pkt *pkt, uint8_t next_header_proto)

View file

@ -127,7 +127,6 @@ struct net_ipv4_igmp_v3_report {
* @param id Fragment id
* @param flags Fragmentation flags
* @param offset Fragment offset
* @param ttl Time-to-live value
*
* @return 0 on success, negative errno otherwise.
*/
@ -138,8 +137,7 @@ int net_ipv4_create_full(struct net_pkt *pkt,
uint8_t tos,
uint16_t id,
uint8_t flags,
uint16_t offset,
uint8_t ttl);
uint16_t offset);
#else
static inline int net_ipv4_create_full(struct net_pkt *pkt,
const struct in_addr *src,
@ -147,8 +145,7 @@ static inline int net_ipv4_create_full(struct net_pkt *pkt,
uint8_t tos,
uint16_t id,
uint8_t flags,
uint16_t offset,
uint8_t ttl)
uint16_t offset)
{
ARG_UNUSED(pkt);
ARG_UNUSED(src);
@ -157,7 +154,6 @@ static inline int net_ipv4_create_full(struct net_pkt *pkt,
ARG_UNUSED(id);
ARG_UNUSED(flags);
ARG_UNUSED(offset);
ARG_UNUSED(ttl);
return -ENOTSUP;
}

View file

@ -42,6 +42,12 @@ LOG_MODULE_REGISTER(net_ctx, CONFIG_NET_CONTEXT_LOG_LEVEL);
#include "tcp.h"
#endif
#ifdef CONFIG_NET_INITIAL_MCAST_TTL
#define INITIAL_MCAST_TTL CONFIG_NET_INITIAL_MCAST_TTL
#else
#define INITIAL_MCAST_TTL 1
#endif
#ifndef EPFNOSUPPORT
/* Some old versions of newlib haven't got this defined in errno.h,
* Just use EPROTONOSUPPORT in this case
@ -479,6 +485,8 @@ int net_context_get(sa_family_t family, enum net_sock_type type, uint16_t proto,
ret = -EADDRINUSE;
break;
}
contexts[i].ipv4_mcast_ttl = INITIAL_MCAST_TTL;
}
}
@ -1544,6 +1552,26 @@ static int get_context_dscp_ecn(struct net_context *context,
#endif
}
static int get_context_mcast_ttl(struct net_context *context,
void *value, size_t *len)
{
#if defined(CONFIG_NET_IPV4)
*((int *)value) = context->ipv4_mcast_ttl;
if (len) {
*len = sizeof(int);
}
return 0;
#else
ARG_UNUSED(context);
ARG_UNUSED(value);
ARG_UNUSED(len);
return -ENOTSUP;
#endif
}
static int get_context_reuseaddr(struct net_context *context,
void *value, size_t *len)
{
@ -2712,6 +2740,24 @@ static int set_context_dscp_ecn(struct net_context *context,
#endif
}
static int set_context_mcast_ttl(struct net_context *context,
const void *value, size_t len)
{
#if defined(CONFIG_NET_IPV4)
uint8_t mcast_ttl = *((int *)value);
len = sizeof(context->ipv4_mcast_ttl);
return set_uint8_option(&context->ipv4_mcast_ttl, &mcast_ttl, 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)
{
@ -2807,6 +2853,9 @@ int net_context_set_option(struct net_context *context,
case NET_OPT_DSCP_ECN:
ret = set_context_dscp_ecn(context, value, len);
break;
case NET_OPT_MCAST_TTL:
ret = set_context_mcast_ttl(context, value, len);
break;
case NET_OPT_REUSEADDR:
ret = set_context_reuseaddr(context, value, len);
break;
@ -2865,6 +2914,9 @@ int net_context_get_option(struct net_context *context,
case NET_OPT_DSCP_ECN:
ret = get_context_dscp_ecn(context, value, len);
break;
case NET_OPT_MCAST_TTL:
ret = get_context_mcast_ttl(context, value, len);
break;
case NET_OPT_REUSEADDR:
ret = get_context_reuseaddr(context, value, len);
break;

View file

@ -3151,6 +3151,47 @@ out:
#endif
}
uint8_t net_if_ipv4_get_mcast_ttl(struct net_if *iface)
{
#if defined(CONFIG_NET_NATIVE_IPV4)
int ret = 0;
net_if_lock(iface);
if (!iface->config.ip.ipv4) {
goto out;
}
ret = iface->config.ip.ipv4->mcast_ttl;
out:
net_if_unlock(iface);
return ret;
#else
ARG_UNUSED(iface);
return 0;
#endif
}
void net_if_ipv4_set_mcast_ttl(struct net_if *iface, uint8_t ttl)
{
#if defined(CONFIG_NET_NATIVE_IPV4)
net_if_lock(iface);
if (!iface->config.ip.ipv4) {
goto out;
}
iface->config.ip.ipv4->mcast_ttl = ttl;
out:
net_if_unlock(iface);
#else
ARG_UNUSED(iface);
ARG_UNUSED(ttl);
#endif
}
struct net_if_router *net_if_ipv4_router_lookup(struct net_if *iface,
struct in_addr *addr)
{
@ -4035,6 +4076,7 @@ static void iface_ipv4_init(int if_count)
for (i = 0; i < ARRAY_SIZE(ipv4_addresses); i++) {
ipv4_addresses[i].ipv4.ttl = CONFIG_NET_INITIAL_TTL;
ipv4_addresses[i].ipv4.mcast_ttl = CONFIG_NET_INITIAL_MCAST_TTL;
}
}

View file

@ -207,7 +207,7 @@ static int interface_send(struct net_if *iface, struct net_pkt *pkt)
ret = net_ipv4_create_full(tmp, ctx->my4addr,
&ctx->peer.in_addr,
tos, 0U, NET_IPV4_DF,
0U, net_pkt_ipv4_ttl(tmp));
0U);
if (ret < 0) {
goto out;
}

View file

@ -2517,6 +2517,16 @@ int zsock_getsockopt_ctx(struct net_context *ctx, int level, int optname,
}
break;
case IP_MULTICAST_TTL:
ret = net_context_get_option(ctx, NET_OPT_MCAST_TTL,
optval, optlen);
if (ret < 0) {
errno = -ret;
return -1;
}
return 0;
}
break;
@ -2885,6 +2895,16 @@ int zsock_setsockopt_ctx(struct net_context *ctx, int level, int optname,
}
break;
case IP_MULTICAST_TTL:
ret = net_context_set_option(ctx, NET_OPT_MCAST_TTL,
optval, optlen);
if (ret < 0) {
errno = -ret;
return -1;
}
return 0;
}
break;

View file

@ -207,8 +207,10 @@ static int get_ipv4_reply(struct net_if *iface,
ipv4_hdr = net_pkt_cursor_get_pos(reply);
*hdr_ipv4 = ipv4_hdr;
net_pkt_set_ipv4_ttl(reply, 1U);
ret = net_ipv4_create_full(reply, src4, dest4, params->tc_tos,
params->identifier, 0, 0, 1);
params->identifier, 0, 0);
if (ret < 0) {
LOG_ERR("Cannot create IPv4 pkt (%d)", ret);
return ret;