net: socket: Add IPv4 multicast join/leave via socket

Zephyr has its own multicast join/leave API but for
interoperability, it is possible to use the multicast
socket API and IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP
socket options.

Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
This commit is contained in:
Jukka Rissanen 2023-11-30 21:31:02 +02:00 committed by Fabio Baltieri
parent 837698b935
commit b58bddb85c
2 changed files with 84 additions and 0 deletions

View File

@ -1117,6 +1117,16 @@ struct in_pktinfo {
/** sockopt: Set IPv4 multicast TTL value. */
#define IP_MULTICAST_TTL 33
/** sockopt: Join IPv4 multicast group. */
#define IP_ADD_MEMBERSHIP 35
/** sockopt: Leave IPv4 multicast group. */
#define IP_DROP_MEMBERSHIP 36
struct ip_mreqn {
struct in_addr imr_multiaddr; /* IP multicast group address */
struct in_addr imr_address; /* IP address of local interface */
int imr_ifindex; /* interface index */
};
/* Socket options for IPPROTO_IPV6 level */
/** sockopt: Set the unicast hop limit for the socket. */

View File

@ -29,6 +29,8 @@ LOG_MODULE_REGISTER(net_sock, CONFIG_NET_SOCKETS_LOG_LEVEL);
#include "socks.h"
#endif
#include <zephyr/net/igmp.h>
#include "../../ip/net_stats.h"
#include "sockets_internal.h"
@ -2642,6 +2644,62 @@ int z_vrfy_zsock_getsockopt(int sock, int level, int optname,
#include <syscalls/zsock_getsockopt_mrsh.c>
#endif /* CONFIG_USERSPACE */
static int ipv4_multicast_group(struct net_context *ctx, const void *optval,
socklen_t optlen, bool do_join)
{
struct ip_mreqn *mreqn;
struct net_if *iface;
int ifindex, ret;
if (optval == NULL || optlen != sizeof(struct ip_mreqn)) {
errno = EINVAL;
return -1;
}
mreqn = (struct ip_mreqn *)optval;
if (mreqn->imr_multiaddr.s_addr == INADDR_ANY) {
errno = EINVAL;
return -1;
}
if (mreqn->imr_ifindex != 0) {
iface = net_if_get_by_index(mreqn->imr_ifindex);
} else {
ifindex = net_if_ipv4_addr_lookup_by_index(&mreqn->imr_address);
iface = net_if_get_by_index(ifindex);
}
if (iface == NULL) {
/* Check if ctx has already an interface and if not,
* then select the default interface.
*/
if (ctx->iface <= 0) {
iface = net_if_get_default();
} else {
iface = net_if_get_by_index(ctx->iface);
}
if (iface == NULL) {
errno = EINVAL;
return -1;
}
}
if (do_join) {
ret = net_ipv4_igmp_join(iface, &mreqn->imr_multiaddr, NULL);
} else {
ret = net_ipv4_igmp_leave(iface, &mreqn->imr_multiaddr);
}
if (ret < 0) {
errno = -ret;
return -1;
}
return 0;
}
int zsock_setsockopt_ctx(struct net_context *ctx, int level, int optname,
const void *optval, socklen_t optlen)
{
@ -2947,6 +3005,22 @@ int zsock_setsockopt_ctx(struct net_context *ctx, int level, int optname,
}
return 0;
case IP_ADD_MEMBERSHIP:
if (IS_ENABLED(CONFIG_NET_IPV4)) {
return ipv4_multicast_group(ctx, optval,
optlen, true);
}
break;
case IP_DROP_MEMBERSHIP:
if (IS_ENABLED(CONFIG_NET_IPV4)) {
return ipv4_multicast_group(ctx, optval,
optlen, false);
}
break;
}
break;