udp: modify ipv4 multicast to allow different conn to join simultaneously

add ref count for ipv4 multicast and leave the multicast group when close
behavior alignment with linux.

Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
This commit is contained in:
zhanghongyu 2023-11-01 19:57:27 +08:00 committed by Xiang Xiao
parent 7c322c250a
commit 43ecf36d78
8 changed files with 96 additions and 5 deletions

View File

@ -117,6 +117,7 @@ struct igmp_group_s
uint8_t ifindex; /* Interface index */ uint8_t ifindex; /* Interface index */
uint8_t flags; /* See IGMP_ flags definitions */ uint8_t flags; /* See IGMP_ flags definitions */
uint8_t msgid; /* Pending message ID (if non-zero) */ uint8_t msgid; /* Pending message ID (if non-zero) */
uint8_t njoins; /* Number of joins from this host */
}; };
/**************************************************************************** /****************************************************************************

View File

@ -160,12 +160,12 @@ int igmp_joingroup(struct net_driver_s *dev,
/* Add the group (MAC) address to the ether drivers MAC filter list */ /* Add the group (MAC) address to the ether drivers MAC filter list */
igmp_addmcastmac(dev, (FAR in_addr_t *)&grpaddr->s_addr); igmp_addmcastmac(dev, (FAR in_addr_t *)&grpaddr->s_addr);
return OK;
} }
/* Return EEXIST if the address is already a member of the group */ DEBUGASSERT(group->njoins < UINT8_MAX);
group->njoins++;
return -EEXIST; return OK;
} }
#endif /* CONFIG_NET_IGMP */ #endif /* CONFIG_NET_IGMP */

View File

@ -138,6 +138,18 @@ int igmp_leavegroup(struct net_driver_s *dev,
ninfo("Leaving group: %p\n", group); ninfo("Leaving group: %p\n", group);
if (group) if (group)
{ {
DEBUGASSERT(group->njoins > 0);
group->njoins--;
/* Take no further actions if there are other members of this group
* on this host.
*/
if (group->njoins > 0)
{
return OK;
}
/* Cancel the timer and discard any queued Membership Reports. /* Cancel the timer and discard any queued Membership Reports.
* Canceling the timer will prevent any new Membership Reports from * Canceling the timer will prevent any new Membership Reports from
* being sent; clearing the flags will discard any pending Membership * being sent; clearing the flags will discard any pending Membership

View File

@ -126,6 +126,7 @@ int ipv4_setsockopt(FAR struct socket *psock, int option,
} }
break; break;
#ifdef NET_UDP_HAVE_STACK
case IP_ADD_MEMBERSHIP: /* Join a multicast group */ case IP_ADD_MEMBERSHIP: /* Join a multicast group */
case IP_DROP_MEMBERSHIP: /* Leave a multicast group */ case IP_DROP_MEMBERSHIP: /* Leave a multicast group */
{ {
@ -142,6 +143,8 @@ int ipv4_setsockopt(FAR struct socket *psock, int option,
} }
else else
{ {
FAR struct udp_conn_s *conn = psock->s_conn;
/* Use the default network device is imr_interface is /* Use the default network device is imr_interface is
* INADDRY_ANY. * INADDRY_ANY.
*/ */
@ -166,17 +169,33 @@ int ipv4_setsockopt(FAR struct socket *psock, int option,
} }
else if (option == IP_ADD_MEMBERSHIP) else if (option == IP_ADD_MEMBERSHIP)
{ {
ret = igmp_joingroup(dev, &mrec->imr_multiaddr); if (conn->mreq.imr_multiaddr.s_addr != 0)
{
ret = -EADDRINUSE;
}
else
{
ret = igmp_joingroup(dev, &mrec->imr_multiaddr);
if (ret == OK)
{
conn->mreq.imr_multiaddr = mrec->imr_multiaddr;
conn->mreq.imr_ifindex = dev->d_ifindex;
}
}
} }
else else
{ {
ret = igmp_leavegroup(dev, &mrec->imr_multiaddr); ret = igmp_leavegroup(dev, &mrec->imr_multiaddr);
if (ret == OK)
{
conn->mreq.imr_multiaddr.s_addr = 0;
conn->mreq.imr_ifindex = 0;
}
} }
} }
} }
break; break;
#ifdef NET_UDP_HAVE_STACK
case IP_MULTICAST_TTL: /* Set/read the time-to-live value of case IP_MULTICAST_TTL: /* Set/read the time-to-live value of
* outgoing multicast packets */ * outgoing multicast packets */
#endif #endif

View File

@ -1207,6 +1207,8 @@ static int netdev_imsf_ioctl(FAR struct socket *psock, int cmd,
ninfo("cmd: %d\n", cmd); ninfo("cmd: %d\n", cmd);
net_lock();
/* Execute the command */ /* Execute the command */
switch (cmd) switch (cmd)
@ -1235,6 +1237,7 @@ static int netdev_imsf_ioctl(FAR struct socket *psock, int cmd,
break; break;
} }
net_unlock();
return ret; return ret;
} }
#endif #endif

View File

@ -310,6 +310,28 @@ int udp_bind(FAR struct udp_conn_s *conn, FAR const struct sockaddr *addr);
int udp_connect(FAR struct udp_conn_s *conn, int udp_connect(FAR struct udp_conn_s *conn,
FAR const struct sockaddr *addr); FAR const struct sockaddr *addr);
#if defined(CONFIG_NET_IGMP)
/****************************************************************************
* Name: udp_leavegroup
*
* Description:
* This function leaves the multicast group to which the conn belongs.
*
* Input Parameters:
* conn - A reference to UDP connection structure. A value of NULL will
* disconnect from any previously connected address.
*
* Assumptions:
* This function is called (indirectly) from user code. Interrupts may
* be enabled.
*
****************************************************************************/
void udp_leavegroup(FAR struct udp_conn_s *conn);
#else
#define udp_leavegroup(c)
#endif
/**************************************************************************** /****************************************************************************
* Name: udp_close * Name: udp_close
* *

View File

@ -103,6 +103,8 @@ int udp_close(FAR struct socket *psock)
nerr("ERROR: udp_txdrain() failed: %d\n", ret); nerr("ERROR: udp_txdrain() failed: %d\n", ret);
} }
udp_leavegroup(conn);
#ifdef CONFIG_NET_UDP_WRITE_BUFFERS #ifdef CONFIG_NET_UDP_WRITE_BUFFERS
/* Free any semi-permanent write buffer callback in place. */ /* Free any semi-permanent write buffer callback in place. */

View File

@ -68,6 +68,7 @@
#include "nat/nat.h" #include "nat/nat.h"
#include "netdev/netdev.h" #include "netdev/netdev.h"
#include "socket/socket.h" #include "socket/socket.h"
#include "igmp/igmp.h"
#include "udp/udp.h" #include "udp/udp.h"
/**************************************************************************** /****************************************************************************
@ -1080,4 +1081,35 @@ int udp_connect(FAR struct udp_conn_s *conn, FAR const struct sockaddr *addr)
return OK; return OK;
} }
#if defined(CONFIG_NET_IGMP)
/****************************************************************************
* Name: udp_leavegroup
*
* Description:
* This function leaves the multicast group to which the conn belongs.
*
* Input Parameters:
* conn - A reference to UDP connection structure. A value of NULL will
* disconnect from any previously connected address.
*
* Assumptions:
* This function is called (indirectly) from user code. Interrupts may
* be enabled.
*
****************************************************************************/
void udp_leavegroup(FAR struct udp_conn_s *conn)
{
if (conn->mreq.imr_multiaddr.s_addr != 0)
{
FAR struct net_driver_s *dev;
if ((dev = netdev_findbyindex(conn->mreq.imr_ifindex)) != NULL)
{
igmp_leavegroup(dev, &conn->mreq.imr_multiaddr);
}
}
}
#endif
#endif /* CONFIG_NET && CONFIG_NET_UDP */ #endif /* CONFIG_NET && CONFIG_NET_UDP */