Skip to content

Commit b027710

Browse files
committed
net: socket: Add support for IPV6_MULTICAST_IF option
Allow user to set the network interface for multicast sockets of type SOCK_DGRAM. Signed-off-by: Jukka Rissanen <[email protected]>
1 parent 2f800ce commit b027710

File tree

4 files changed

+169
-6
lines changed

4 files changed

+169
-6
lines changed

include/zephyr/net/net_context.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,12 @@ __net_socket struct net_context {
361361
* see RFC 5014 for details.
362362
*/
363363
uint16_t addr_preferences;
364+
365+
/**
366+
* IPv6 multicast output network interface for this context/socket.
367+
* Only allowed for SOCK_DGRAM or SOCK_RAW type sockets.
368+
*/
369+
uint8_t ipv6_mcast_ifindex;
364370
#endif
365371
#if defined(CONFIG_NET_CONTEXT_TIMESTAMPING)
366372
/** Enable RX, TX or both timestamps of packets send through sockets. */
@@ -1292,6 +1298,7 @@ enum net_context_option {
12921298
NET_OPT_TTL = 16, /**< IPv4 unicast TTL */
12931299
NET_OPT_ADDR_PREFERENCES = 17, /**< IPv6 address preference */
12941300
NET_OPT_TIMESTAMPING = 18, /**< Packet timestamping */
1301+
NET_OPT_MCAST_IFINDEX = 19, /**< IPv6 multicast output network interface index */
12951302
NET_OPT_MTU = 20, /**< IPv4 socket path MTU */
12961303
};
12971304

include/zephyr/net/socket.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,9 @@ struct ip_mreqn {
12221222
/** Set the unicast hop limit for the socket. */
12231223
#define IPV6_UNICAST_HOPS 16
12241224

1225+
/** Set multicast output network interface index for the socket. */
1226+
#define IPV6_MULTICAST_IF 17
1227+
12251228
/** Set the multicast hop limit for the socket. */
12261229
#define IPV6_MULTICAST_HOPS 18
12271230

subsys/net/ip/net_context.c

Lines changed: 137 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,17 @@ int net_context_bind(struct net_context *context, const struct sockaddr *addr,
767767
if (net_ipv6_is_addr_mcast(&addr6->sin6_addr)) {
768768
struct net_if_mcast_addr *maddr;
769769

770+
if (IS_ENABLED(CONFIG_NET_UDP) &&
771+
net_context_get_type(context) == SOCK_DGRAM) {
772+
if (COND_CODE_1(CONFIG_NET_IPV6,
773+
(context->options.ipv6_mcast_ifindex > 0),
774+
(false))) {
775+
IF_ENABLED(CONFIG_NET_IPV6,
776+
(iface = net_if_get_by_index(
777+
context->options.ipv6_mcast_ifindex)));
778+
}
779+
}
780+
770781
maddr = net_if_ipv6_maddr_lookup(&addr6->sin6_addr,
771782
&iface);
772783
if (!maddr) {
@@ -1830,6 +1841,55 @@ static int get_context_mtu(struct net_context *context,
18301841
return 0;
18311842
}
18321843

1844+
static int get_context_mcast_ifindex(struct net_context *context,
1845+
void *value, size_t *len)
1846+
{
1847+
#if defined(CONFIG_NET_IPV6)
1848+
if (net_context_get_family(context) != AF_INET6) {
1849+
return -EAFNOSUPPORT;
1850+
}
1851+
1852+
/* If user has not set the ifindex, then get the interface
1853+
* that this socket is bound to.
1854+
*/
1855+
if (context->options.ipv6_mcast_ifindex == 0) {
1856+
struct net_if *iface;
1857+
int ifindex;
1858+
1859+
if (net_context_is_bound_to_iface(context)) {
1860+
iface = net_context_get_iface(context);
1861+
} else {
1862+
iface = net_if_get_default();
1863+
}
1864+
1865+
if (!net_if_flag_is_set(iface, NET_IF_IPV6)) {
1866+
return -EPROTOTYPE;
1867+
}
1868+
1869+
ifindex = net_if_get_by_iface(iface);
1870+
if (ifindex < 1) {
1871+
return -ENOENT;
1872+
}
1873+
1874+
*((int *)value) = ifindex;
1875+
} else {
1876+
*((int *)value) = context->options.ipv6_mcast_ifindex;
1877+
}
1878+
1879+
if (len) {
1880+
*len = sizeof(int);
1881+
}
1882+
1883+
return 0;
1884+
#else
1885+
ARG_UNUSED(context);
1886+
ARG_UNUSED(value);
1887+
ARG_UNUSED(len);
1888+
1889+
return -ENOTSUP;
1890+
#endif
1891+
}
1892+
18331893
/* If buf is not NULL, then use it. Otherwise read the data to be written
18341894
* to net_pkt from msghdr.
18351895
*/
@@ -2007,7 +2067,7 @@ static int context_sendto(struct net_context *context,
20072067
bool sendto)
20082068
{
20092069
const struct msghdr *msghdr = NULL;
2010-
struct net_if *iface;
2070+
struct net_if *iface = NULL;
20112071
struct net_pkt *pkt = NULL;
20122072
sa_family_t family;
20132073
size_t tmp_len;
@@ -2067,18 +2127,31 @@ static int context_sendto(struct net_context *context,
20672127
return -EDESTADDRREQ;
20682128
}
20692129

2130+
if (IS_ENABLED(CONFIG_NET_UDP) &&
2131+
net_context_get_type(context) == SOCK_DGRAM) {
2132+
if (net_ipv6_is_addr_mcast(&addr6->sin6_addr) &&
2133+
COND_CODE_1(CONFIG_NET_IPV6,
2134+
(context->options.ipv6_mcast_ifindex > 0), (false))) {
2135+
IF_ENABLED(CONFIG_NET_IPV6,
2136+
(iface = net_if_get_by_index(
2137+
context->options.ipv6_mcast_ifindex)));
2138+
}
2139+
}
2140+
20702141
/* If application has not yet set the destination address
20712142
* i.e., by not calling connect(), then set the interface
20722143
* here so that the packet gets sent to the correct network
20732144
* interface. This issue can be seen if there are multiple
20742145
* network interfaces and we are trying to send data to
20752146
* second or later network interface.
20762147
*/
2077-
if (net_ipv6_is_addr_unspecified(
2078-
&net_sin6(&context->remote)->sin6_addr) &&
2079-
!net_context_is_bound_to_iface(context)) {
2080-
iface = net_if_ipv6_select_src_iface(&addr6->sin6_addr);
2081-
net_context_set_iface(context, iface);
2148+
if (iface == NULL) {
2149+
if (net_ipv6_is_addr_unspecified(
2150+
&net_sin6(&context->remote)->sin6_addr) &&
2151+
!net_context_is_bound_to_iface(context)) {
2152+
iface = net_if_ipv6_select_src_iface(&addr6->sin6_addr);
2153+
net_context_set_iface(context, iface);
2154+
}
20822155
}
20832156

20842157
} else if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) {
@@ -3211,6 +3284,58 @@ static int set_context_timestamping(struct net_context *context,
32113284
#endif
32123285
}
32133286

3287+
static int set_context_mcast_ifindex(struct net_context *context,
3288+
const void *value, size_t len)
3289+
{
3290+
#if defined(CONFIG_NET_IPV6)
3291+
int mcast_ifindex = *((int *)value);
3292+
enum net_sock_type type;
3293+
struct net_if *iface;
3294+
3295+
if (net_context_get_family(context) != AF_INET6) {
3296+
return -EAFNOSUPPORT;
3297+
}
3298+
3299+
if (len != sizeof(int)) {
3300+
return -EINVAL;
3301+
}
3302+
3303+
type = net_context_get_type(context);
3304+
if (type != SOCK_DGRAM && type != SOCK_RAW) {
3305+
return -EINVAL;
3306+
}
3307+
3308+
/* optlen equal to 0 then remove the binding */
3309+
if (mcast_ifindex == 0) {
3310+
context->options.ipv6_mcast_ifindex = 0;
3311+
return 0;
3312+
}
3313+
3314+
if (mcast_ifindex < 1 || mcast_ifindex > 255) {
3315+
return -EINVAL;
3316+
}
3317+
3318+
iface = net_if_get_by_index(mcast_ifindex);
3319+
if (iface == NULL) {
3320+
return -ENOENT;
3321+
}
3322+
3323+
if (!net_if_flag_is_set(iface, NET_IF_IPV6)) {
3324+
return -EPROTOTYPE;
3325+
}
3326+
3327+
context->options.ipv6_mcast_ifindex = mcast_ifindex;
3328+
3329+
return 0;
3330+
#else
3331+
ARG_UNUSED(context);
3332+
ARG_UNUSED(value);
3333+
ARG_UNUSED(len);
3334+
3335+
return -ENOTSUP;
3336+
#endif
3337+
}
3338+
32143339
int net_context_set_option(struct net_context *context,
32153340
enum net_context_option option,
32163341
const void *value, size_t len)
@@ -3290,6 +3415,9 @@ int net_context_set_option(struct net_context *context,
32903415
ret = set_context_ipv6_mtu(context, value, len);
32913416
}
32923417

3418+
break;
3419+
case NET_OPT_MCAST_IFINDEX:
3420+
ret = set_context_mcast_ifindex(context, value, len);
32933421
break;
32943422
}
32953423

@@ -3370,6 +3498,9 @@ int net_context_get_option(struct net_context *context,
33703498
case NET_OPT_MTU:
33713499
ret = get_context_mtu(context, value, len);
33723500
break;
3501+
case NET_OPT_MCAST_IFINDEX:
3502+
ret = get_context_mcast_ifindex(context, value, len);
3503+
break;
33733504
}
33743505

33753506
k_mutex_unlock(&context->lock);

subsys/net/lib/sockets/sockets_inet.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,6 +1931,17 @@ int zsock_getsockopt_ctx(struct net_context *ctx, int level, int optname,
19311931

19321932
return 0;
19331933

1934+
case IPV6_MULTICAST_IF:
1935+
ret = net_context_get_option(ctx,
1936+
NET_OPT_MCAST_IFINDEX,
1937+
optval, optlen);
1938+
if (ret < 0) {
1939+
errno = -ret;
1940+
return -1;
1941+
}
1942+
1943+
return 0;
1944+
19341945
case IPV6_MULTICAST_HOPS:
19351946
ret = net_context_get_option(ctx,
19361947
NET_OPT_MCAST_HOP_LIMIT,
@@ -2524,6 +2535,17 @@ int zsock_setsockopt_ctx(struct net_context *ctx, int level, int optname,
25242535

25252536
return 0;
25262537

2538+
case IPV6_MULTICAST_IF:
2539+
ret = net_context_set_option(ctx,
2540+
NET_OPT_MCAST_IFINDEX,
2541+
optval, optlen);
2542+
if (ret < 0) {
2543+
errno = -ret;
2544+
return -1;
2545+
}
2546+
2547+
return 0;
2548+
25272549
case IPV6_MULTICAST_HOPS:
25282550
ret = net_context_set_option(ctx,
25292551
NET_OPT_MCAST_HOP_LIMIT,

0 commit comments

Comments
 (0)