From de0268def0c38df8b85fa465cadf9441a6b11e6b Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Mon, 27 Nov 2023 17:23:15 +0200 Subject: [PATCH] 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 --- include/zephyr/net/net_context.h | 17 +++++++++- include/zephyr/net/net_if.h | 20 ++++++++++++ include/zephyr/net/socket.h | 3 ++ subsys/net/ip/Kconfig.ipv4 | 21 +++++++++++-- subsys/net/ip/igmp.c | 11 +++++-- subsys/net/ip/ipv4.c | 26 +++++++++++----- subsys/net/ip/ipv4.h | 8 ++--- subsys/net/ip/net_context.c | 52 +++++++++++++++++++++++++++++++ subsys/net/ip/net_if.c | 42 +++++++++++++++++++++++++ subsys/net/l2/virtual/ipip/ipip.c | 2 +- subsys/net/lib/sockets/sockets.c | 20 ++++++++++++ tests/net/icmp/src/main.c | 4 ++- 12 files changed, 205 insertions(+), 21 deletions(-) diff --git a/include/zephyr/net/net_context.h b/include/zephyr/net/net_context.h index 7e73bf8eee..f089658df3 100644 --- a/include/zephyr/net/net_context.h +++ b/include/zephyr/net/net_context.h @@ -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, }; /** diff --git a/include/zephyr/net/net_if.h b/include/zephyr/net/net_if.h index 1780adeb5c..e5557b97fb 100644 --- a/include/zephyr/net/net_if.h +++ b/include/zephyr/net/net_if.h @@ -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. * diff --git a/include/zephyr/net/socket.h b/include/zephyr/net/socket.h index 40458986be..70d7eb9a1e 100644 --- a/include/zephyr/net/socket.h +++ b/include/zephyr/net/socket.h @@ -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 diff --git a/subsys/net/ip/Kconfig.ipv4 b/subsys/net/ip/Kconfig.ipv4 index e5d30cfc27..69b0c260ca 100644 --- a/subsys/net/ip/Kconfig.ipv4 +++ b/subsys/net/ip/Kconfig.ipv4 @@ -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" diff --git a/subsys/net/ip/igmp.c b/subsys/net/ip/igmp.c index 3cd7be7de4..91a7a4cad5 100644 --- a/subsys/net/ip/igmp.c +++ b/subsys/net/ip/igmp.c @@ -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; } diff --git a/subsys/net/ip/ipv4.c b/subsys/net/ip/ipv4.c index ce5c1e9986..fad04229c9 100644 --- a/subsys/net/ip/ipv4.c +++ b/subsys/net/ip/ipv4.c @@ -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) diff --git a/subsys/net/ip/ipv4.h b/subsys/net/ip/ipv4.h index 1d7d64fd0b..162faca24b 100644 --- a/subsys/net/ip/ipv4.h +++ b/subsys/net/ip/ipv4.h @@ -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; } diff --git a/subsys/net/ip/net_context.c b/subsys/net/ip/net_context.c index 6c2258fcd9..b62035496a 100644 --- a/subsys/net/ip/net_context.c +++ b/subsys/net/ip/net_context.c @@ -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; diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index f69fc2b871..1721ee0de5 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -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; } } diff --git a/subsys/net/l2/virtual/ipip/ipip.c b/subsys/net/l2/virtual/ipip/ipip.c index 41c3b8f41f..e3514ea82d 100644 --- a/subsys/net/l2/virtual/ipip/ipip.c +++ b/subsys/net/l2/virtual/ipip/ipip.c @@ -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; } diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c index 02fa21675e..ac2f26c16a 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -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; diff --git a/tests/net/icmp/src/main.c b/tests/net/icmp/src/main.c index 61a749c27e..1e1dd690a0 100644 --- a/tests/net/icmp/src/main.c +++ b/tests/net/icmp/src/main.c @@ -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;