Skip to content

Commit d94b35a

Browse files
committed
net: socket: Add support for IP_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 42bcd49 commit d94b35a

File tree

4 files changed

+255
-74
lines changed

4 files changed

+255
-74
lines changed

include/zephyr/net/net_context.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -361,13 +361,23 @@ __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;
370364
#endif
365+
#if defined(CONFIG_NET_IPV6) || defined(CONFIG_NET_IPV4)
366+
union {
367+
/**
368+
* IPv6 multicast output network interface for this context/socket.
369+
* Only allowed for SOCK_DGRAM or SOCK_RAW type sockets.
370+
*/
371+
uint8_t ipv6_mcast_ifindex;
372+
373+
/**
374+
* IPv4 multicast output network interface for this context/socket.
375+
* Only allowed for SOCK_DGRAM type sockets.
376+
*/
377+
uint8_t ipv4_mcast_ifindex;
378+
};
379+
#endif /* CONFIG_NET_IPV6 || CONFIG_NET_IPV4 */
380+
371381
#if defined(CONFIG_NET_CONTEXT_TIMESTAMPING)
372382
/** Enable RX, TX or both timestamps of packets send through sockets. */
373383
uint8_t timestamping;

include/zephyr/net/socket.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,8 @@ struct in_pktinfo {
11961196
*/
11971197
#define IP_MTU 14
11981198

1199+
/** Set IPv4 multicast datagram network interface. */
1200+
#define IP_MULTICAST_IF 32
11991201
/** Set IPv4 multicast TTL value. */
12001202
#define IP_MULTICAST_TTL 33
12011203
/** Join IPv4 multicast group. */
@@ -1212,6 +1214,14 @@ struct ip_mreqn {
12121214
int imr_ifindex; /**< Network interface index */
12131215
};
12141216

1217+
/**
1218+
* @brief Struct used when setting a IPv4 multicast network interface.
1219+
*/
1220+
struct ip_mreq {
1221+
struct in_addr imr_multiaddr; /**< IP multicast group address */
1222+
struct in_addr imr_interface; /**< IP address of local interface */
1223+
};
1224+
12151225
/** @} */
12161226

12171227
/**

subsys/net/ip/net_context.c

Lines changed: 102 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,17 @@ int net_context_bind(struct net_context *context, const struct sockaddr *addr,
880880
if (net_ipv4_is_addr_mcast(&addr4->sin_addr)) {
881881
struct net_if_mcast_addr *maddr;
882882

883+
if (IS_ENABLED(CONFIG_NET_UDP) &&
884+
net_context_get_type(context) == SOCK_DGRAM) {
885+
if (COND_CODE_1(CONFIG_NET_IPV4,
886+
(context->options.ipv4_mcast_ifindex > 0),
887+
(false))) {
888+
IF_ENABLED(CONFIG_NET_IPV4,
889+
(iface = net_if_get_by_index(
890+
context->options.ipv4_mcast_ifindex)));
891+
}
892+
}
893+
883894
maddr = net_if_ipv4_maddr_lookup(&addr4->sin_addr,
884895
&iface);
885896
if (!maddr) {
@@ -1844,43 +1855,52 @@ static int get_context_mtu(struct net_context *context,
18441855
static int get_context_mcast_ifindex(struct net_context *context,
18451856
void *value, size_t *len)
18461857
{
1847-
#if defined(CONFIG_NET_IPV6)
1848-
if (net_context_get_family(context) != AF_INET6) {
1849-
return -EAFNOSUPPORT;
1850-
}
1858+
#if defined(CONFIG_NET_IPV6) || defined(CONFIG_NET_IPV4)
1859+
sa_family_t family = net_context_get_family(context);
18511860

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;
1861+
if ((IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) ||
1862+
(IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET)) {
1863+
/* If user has not set the ifindex, then get the interface
1864+
* that this socket is bound to.
1865+
*/
1866+
if (context->options.ipv6_mcast_ifindex == 0) {
1867+
struct net_if *iface;
1868+
int ifindex;
18581869

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-
}
1870+
if (net_context_is_bound_to_iface(context)) {
1871+
iface = net_context_get_iface(context);
1872+
} else {
1873+
iface = net_if_get_default();
1874+
}
18641875

1865-
if (!net_if_flag_is_set(iface, NET_IF_IPV6)) {
1866-
return -EPROTOTYPE;
1867-
}
1876+
if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) {
1877+
if (!net_if_flag_is_set(iface, NET_IF_IPV6)) {
1878+
return -EPROTOTYPE;
1879+
}
1880+
} else if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) {
1881+
if (!net_if_flag_is_set(iface, NET_IF_IPV4)) {
1882+
return -EPROTOTYPE;
1883+
}
1884+
}
18681885

1869-
ifindex = net_if_get_by_iface(iface);
1870-
if (ifindex < 1) {
1871-
return -ENOENT;
1886+
ifindex = net_if_get_by_iface(iface);
1887+
if (ifindex < 1) {
1888+
return -ENOENT;
1889+
}
1890+
1891+
*((int *)value) = ifindex;
1892+
} else {
1893+
*((int *)value) = context->options.ipv6_mcast_ifindex;
18721894
}
18731895

1874-
*((int *)value) = ifindex;
1875-
} else {
1876-
*((int *)value) = context->options.ipv6_mcast_ifindex;
1877-
}
1896+
if (len) {
1897+
*len = sizeof(int);
1898+
}
18781899

1879-
if (len) {
1880-
*len = sizeof(int);
1900+
return 0;
18811901
}
18821902

1883-
return 0;
1903+
return -EAFNOSUPPORT;
18841904
#else
18851905
ARG_UNUSED(context);
18861906
ARG_UNUSED(value);
@@ -2193,17 +2213,30 @@ static int context_sendto(struct net_context *context,
21932213
return -EDESTADDRREQ;
21942214
}
21952215

2216+
if (IS_ENABLED(CONFIG_NET_UDP) &&
2217+
net_context_get_type(context) == SOCK_DGRAM) {
2218+
if (net_ipv4_is_addr_mcast(&addr4->sin_addr) &&
2219+
COND_CODE_1(CONFIG_NET_IPV4,
2220+
(context->options.ipv4_mcast_ifindex > 0), (false))) {
2221+
IF_ENABLED(CONFIG_NET_IPV4,
2222+
(iface = net_if_get_by_index(
2223+
context->options.ipv4_mcast_ifindex)));
2224+
}
2225+
}
2226+
21962227
/* If application has not yet set the destination address
21972228
* i.e., by not calling connect(), then set the interface
21982229
* here so that the packet gets sent to the correct network
21992230
* interface. This issue can be seen if there are multiple
22002231
* network interfaces and we are trying to send data to
22012232
* second or later network interface.
22022233
*/
2203-
if (net_sin(&context->remote)->sin_addr.s_addr == 0U &&
2204-
!net_context_is_bound_to_iface(context)) {
2205-
iface = net_if_ipv4_select_src_iface(&addr4->sin_addr);
2206-
net_context_set_iface(context, iface);
2234+
if (iface == NULL) {
2235+
if (net_sin(&context->remote)->sin_addr.s_addr == 0U &&
2236+
!net_context_is_bound_to_iface(context)) {
2237+
iface = net_if_ipv4_select_src_iface(&addr4->sin_addr);
2238+
net_context_set_iface(context, iface);
2239+
}
22072240
}
22082241

22092242
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) && family == AF_PACKET) {
@@ -3287,46 +3320,55 @@ static int set_context_timestamping(struct net_context *context,
32873320
static int set_context_mcast_ifindex(struct net_context *context,
32883321
const void *value, size_t len)
32893322
{
3290-
#if defined(CONFIG_NET_IPV6)
3323+
#if defined(CONFIG_NET_IPV6) || defined(CONFIG_NET_IPV4)
3324+
sa_family_t family = net_context_get_family(context);
32913325
int mcast_ifindex = *((int *)value);
32923326
enum net_sock_type type;
32933327
struct net_if *iface;
32943328

3295-
if (net_context_get_family(context) != AF_INET6) {
3296-
return -EAFNOSUPPORT;
3297-
}
3329+
if ((IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) ||
3330+
(IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET)) {
32983331

3299-
if (len != sizeof(int)) {
3300-
return -EINVAL;
3301-
}
3332+
if (len != sizeof(int)) {
3333+
return -EINVAL;
3334+
}
33023335

3303-
type = net_context_get_type(context);
3304-
if (type != SOCK_DGRAM && type != SOCK_RAW) {
3305-
return -EINVAL;
3306-
}
3336+
type = net_context_get_type(context);
3337+
if (type != SOCK_DGRAM) {
3338+
return -EINVAL;
3339+
}
33073340

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-
}
3341+
/* optlen equal to 0 then remove the binding */
3342+
if (mcast_ifindex == 0) {
3343+
context->options.ipv6_mcast_ifindex = 0;
3344+
return 0;
3345+
}
33133346

3314-
if (mcast_ifindex < 1 || mcast_ifindex > 255) {
3315-
return -EINVAL;
3316-
}
3347+
if (mcast_ifindex < 1 || mcast_ifindex > 255) {
3348+
return -EINVAL;
3349+
}
33173350

3318-
iface = net_if_get_by_index(mcast_ifindex);
3319-
if (iface == NULL) {
3320-
return -ENOENT;
3321-
}
3351+
iface = net_if_get_by_index(mcast_ifindex);
3352+
if (iface == NULL) {
3353+
return -ENOENT;
3354+
}
33223355

3323-
if (!net_if_flag_is_set(iface, NET_IF_IPV6)) {
3324-
return -EPROTOTYPE;
3325-
}
3356+
if (IS_ENABLED(CONFIG_NET_IPV6) && family == AF_INET6) {
3357+
if (!net_if_flag_is_set(iface, NET_IF_IPV6)) {
3358+
return -EPROTOTYPE;
3359+
}
3360+
} else if (IS_ENABLED(CONFIG_NET_IPV4) && family == AF_INET) {
3361+
if (!net_if_flag_is_set(iface, NET_IF_IPV4)) {
3362+
return -EPROTOTYPE;
3363+
}
3364+
}
33263365

3327-
context->options.ipv6_mcast_ifindex = mcast_ifindex;
3366+
context->options.ipv6_mcast_ifindex = mcast_ifindex;
33283367

3329-
return 0;
3368+
return 0;
3369+
}
3370+
3371+
return -EAFNOSUPPORT;
33303372
#else
33313373
ARG_UNUSED(context);
33323374
ARG_UNUSED(value);

0 commit comments

Comments
 (0)