Skip to content

Commit 41dcd25

Browse files
Merge pull request #5196 from kegilbert/multicast-ipv6-2
Add IPv4 and IPv6 multicast implementation for UDPSocket
2 parents 593eab2 + b56ced2 commit 41dcd25

File tree

6 files changed

+225
-13
lines changed

6 files changed

+225
-13
lines changed

features/FEATURE_LWIP/lwip-interface/lwip_stack.c

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@
3232
#include "lwip/tcp.h"
3333
#include "lwip/ip.h"
3434
#include "lwip/mld6.h"
35+
#include "lwip/igmp.h"
3536
#include "lwip/dns.h"
3637
#include "lwip/udp.h"
38+
#include "lwip_errno.h"
3739
#include "netif/lwip_ethernet.h"
3840
#include "emac_api.h"
3941
#include "ppp_lwip.h"
@@ -46,6 +48,10 @@ static nsapi_error_t mbed_lwip_err_remap(err_t err);
4648
#define MBED_NETIF_INIT_FN eth_arch_enetif_init
4749
#endif
4850

51+
#ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
52+
#define LWIP_SOCKET_MAX_MEMBERSHIPS 4
53+
#endif
54+
4955
/* Static arena of sockets */
5056
static struct lwip_socket {
5157
bool in_use;
@@ -56,13 +62,39 @@ static struct lwip_socket {
5662

5763
void (*cb)(void *);
5864
void *data;
65+
66+
// Track multicast addresses subscribed to by this socket
67+
nsapi_ip_mreq_t *multicast_memberships;
68+
uint32_t multicast_memberships_count;
69+
uint32_t multicast_memberships_registry;
70+
5971
} lwip_arena[MEMP_NUM_NETCONN];
6072

6173
static bool lwip_inited = false;
6274
static bool lwip_connected = false;
6375
static bool netif_inited = false;
6476
static bool netif_is_ppp = false;
6577

78+
static nsapi_error_t mbed_lwip_setsockopt(nsapi_stack_t *stack, nsapi_socket_t handle, int level, int optname, const void *optval, unsigned optlen);
79+
80+
static inline uint32_t next_registered_multicast_member(const struct lwip_socket *s, uint32_t index) {
81+
while (!(s->multicast_memberships_registry & (0x0001 << index))) { index++; }
82+
return index;
83+
}
84+
85+
static inline uint32_t next_free_multicast_member(const struct lwip_socket *s, uint32_t index) {
86+
while ((s->multicast_memberships_registry & (0x0001 << index))) { index++; }
87+
return index;
88+
}
89+
90+
static inline void set_multicast_member_registry_bit(struct lwip_socket *s, uint32_t index) {
91+
s->multicast_memberships_registry |= (0x0001 << index);
92+
}
93+
94+
static inline void clear_multicast_member_registry_bit(struct lwip_socket *s, uint32_t index) {
95+
s->multicast_memberships_registry &= ~(0x0001 << index);
96+
}
97+
6698
static struct lwip_socket *mbed_lwip_arena_alloc(void)
6799
{
68100
sys_prot_t prot = sys_arch_protect();
@@ -84,6 +116,18 @@ static struct lwip_socket *mbed_lwip_arena_alloc(void)
84116
static void mbed_lwip_arena_dealloc(struct lwip_socket *s)
85117
{
86118
s->in_use = false;
119+
120+
while (s->multicast_memberships_count > 0) {
121+
uint32_t index = 0;
122+
index = next_registered_multicast_member(s, index);
123+
124+
mbed_lwip_setsockopt(NULL, s, NSAPI_SOCKET, NSAPI_DROP_MEMBERSHIP, &s->multicast_memberships[index],
125+
sizeof(s->multicast_memberships[index]));
126+
index++;
127+
}
128+
129+
free(s->multicast_memberships);
130+
s->multicast_memberships = NULL;
87131
}
88132

89133
static void mbed_lwip_socket_callback(struct netconn *nc, enum netconn_evt eh, u16_t len)
@@ -1083,6 +1127,24 @@ static nsapi_size_or_error_t mbed_lwip_socket_recvfrom(nsapi_stack_t *stack, nsa
10831127
return recv;
10841128
}
10851129

1130+
static int32_t find_multicast_member(const struct lwip_socket *s, const nsapi_ip_mreq_t *imr) {
1131+
uint32_t count = 0;
1132+
uint32_t index = 0;
1133+
// Set upper limit on while loop, should break out when the membership pair is found
1134+
while (count < s->multicast_memberships_count) {
1135+
index = next_registered_multicast_member(s, index);
1136+
1137+
if (memcmp(&s->multicast_memberships[index].imr_multiaddr, &imr->imr_multiaddr, sizeof(nsapi_addr_t)) == 0 &&
1138+
memcmp(&s->multicast_memberships[index].imr_interface, &imr->imr_interface, sizeof(nsapi_addr_t)) == 0) {
1139+
return index;
1140+
}
1141+
count++;
1142+
index++;
1143+
}
1144+
1145+
return -1;
1146+
}
1147+
10861148
static nsapi_error_t mbed_lwip_setsockopt(nsapi_stack_t *stack, nsapi_socket_t handle, int level, int optname, const void *optval, unsigned optlen)
10871149
{
10881150
struct lwip_socket *s = (struct lwip_socket *)handle;
@@ -1126,6 +1188,103 @@ static nsapi_error_t mbed_lwip_setsockopt(nsapi_stack_t *stack, nsapi_socket_t h
11261188
}
11271189
return 0;
11281190

1191+
case NSAPI_ADD_MEMBERSHIP:
1192+
case NSAPI_DROP_MEMBERSHIP: {
1193+
if (optlen != sizeof(nsapi_ip_mreq_t)) {
1194+
return NSAPI_ERROR_PARAMETER;
1195+
}
1196+
err_t igmp_err;
1197+
const nsapi_ip_mreq_t *imr = optval;
1198+
1199+
/* Check interface address type matches group, or is unspecified */
1200+
if (imr->imr_interface.version != NSAPI_UNSPEC && imr->imr_interface.version != imr->imr_multiaddr.version) {
1201+
return NSAPI_ERROR_PARAMETER;
1202+
}
1203+
1204+
ip_addr_t if_addr;
1205+
ip_addr_t multi_addr;
1206+
1207+
/* Convert the group address */
1208+
if (!convert_mbed_addr_to_lwip(&multi_addr, &imr->imr_multiaddr)) {
1209+
return NSAPI_ERROR_PARAMETER;
1210+
}
1211+
1212+
/* Convert the interface address, or make sure it's the correct sort of "any" */
1213+
if (imr->imr_interface.version != NSAPI_UNSPEC) {
1214+
if (!convert_mbed_addr_to_lwip(&if_addr, &imr->imr_interface)) {
1215+
return NSAPI_ERROR_PARAMETER;
1216+
}
1217+
} else {
1218+
ip_addr_set_any(IP_IS_V6(&if_addr), &if_addr);
1219+
}
1220+
1221+
igmp_err = ERR_USE; // Maps to NSAPI_ERROR_UNSUPPORTED
1222+
int32_t member_pair_index = find_multicast_member(s, imr);
1223+
1224+
if (optname == NSAPI_ADD_MEMBERSHIP) {
1225+
if (!s->multicast_memberships) {
1226+
// First multicast join on this socket, allocate space for membership tracking
1227+
s->multicast_memberships = malloc(sizeof(nsapi_ip_mreq_t) * LWIP_SOCKET_MAX_MEMBERSHIPS);
1228+
if (!s->multicast_memberships) {
1229+
return NSAPI_ERROR_NO_MEMORY;
1230+
}
1231+
} else if(s->multicast_memberships_count == LWIP_SOCKET_MAX_MEMBERSHIPS) {
1232+
return NSAPI_ERROR_NO_MEMORY;
1233+
}
1234+
1235+
if (member_pair_index != -1) {
1236+
return NSAPI_ERROR_ADDRESS_IN_USE;
1237+
}
1238+
1239+
member_pair_index = next_free_multicast_member(s, 0);
1240+
1241+
sys_prot_t prot = sys_arch_protect();
1242+
1243+
#if LWIP_IPV4
1244+
if (IP_IS_V4(&if_addr)) {
1245+
igmp_err = igmp_joingroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr));
1246+
}
1247+
#endif
1248+
#if LWIP_IPV6
1249+
if (IP_IS_V6(&if_addr)) {
1250+
igmp_err = mld6_joingroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr));
1251+
}
1252+
#endif
1253+
1254+
sys_arch_unprotect(prot);
1255+
1256+
if (igmp_err == ERR_OK) {
1257+
set_multicast_member_registry_bit(s, member_pair_index);
1258+
s->multicast_memberships[member_pair_index] = *imr;
1259+
s->multicast_memberships_count++;
1260+
}
1261+
} else {
1262+
if (member_pair_index == -1) {
1263+
return NSAPI_ERROR_NO_ADDRESS;
1264+
}
1265+
1266+
clear_multicast_member_registry_bit(s, member_pair_index);
1267+
s->multicast_memberships_count--;
1268+
1269+
sys_prot_t prot = sys_arch_protect();
1270+
1271+
#if LWIP_IPV4
1272+
if (IP_IS_V4(&if_addr)) {
1273+
igmp_err = igmp_leavegroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr));
1274+
}
1275+
#endif
1276+
#if LWIP_IPV6
1277+
if (IP_IS_V6(&if_addr)) {
1278+
igmp_err = mld6_leavegroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr));
1279+
}
1280+
#endif
1281+
1282+
sys_arch_unprotect(prot);
1283+
}
1284+
1285+
return mbed_lwip_err_remap(igmp_err);
1286+
}
1287+
11291288
default:
11301289
return NSAPI_ERROR_UNSUPPORTED;
11311290
}

features/netsocket/Socket.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,28 @@ nsapi_error_t Socket::close()
7070
return ret;
7171
}
7272

73+
int Socket::modify_multicast_group(const SocketAddress &address, nsapi_socket_option_t socketopt)
74+
{
75+
nsapi_ip_mreq_t mreq;
76+
77+
// Set up group address
78+
mreq.imr_multiaddr = address.get_addr();
79+
mreq.imr_interface = nsapi_addr_t(); // Default address, NSAPI_UNSPEC
80+
81+
return this->setsockopt(NSAPI_SOCKET, socketopt, &mreq, sizeof(mreq));
82+
}
83+
84+
int Socket::join_multicast_group(const SocketAddress &address)
85+
{
86+
return modify_multicast_group(address, NSAPI_ADD_MEMBERSHIP);
87+
}
88+
89+
int Socket::leave_multicast_group(const SocketAddress &address)
90+
{
91+
return modify_multicast_group(address, NSAPI_DROP_MEMBERSHIP);
92+
}
93+
94+
7395
nsapi_error_t Socket::bind(uint16_t port)
7496
{
7597
// Underlying bind is thread safe

features/netsocket/Socket.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ class Socket {
6262
*/
6363
nsapi_error_t close();
6464

65+
/** Subscribes to an IP multicast group
66+
*
67+
* @param address Multicast group IP address
68+
* @return Negative error code on failure
69+
*/
70+
int join_multicast_group(const SocketAddress &address);
71+
72+
/** Leave an IP multicast group
73+
*
74+
* @param address Multicast group IP address
75+
* @return Negative error code on failure
76+
*/
77+
int leave_multicast_group(const SocketAddress &address);
78+
6579
/** Bind a specific address to a socket
6680
*
6781
* Binding a socket specifies the address and port on which to recieve
@@ -203,6 +217,7 @@ class Socket {
203217
Socket();
204218
virtual nsapi_protocol_t get_proto() = 0;
205219
virtual void event() = 0;
220+
int modify_multicast_group(const SocketAddress &address, nsapi_socket_option_t socketopt);
206221

207222
NetworkStack *_stack;
208223
nsapi_socket_t _socket;

features/netsocket/TCPSocket.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ class TCPSocket : public Socket {
5757
*/
5858
virtual ~TCPSocket();
5959

60+
/** Override multicast functions to return error for TCP
61+
*
62+
*/
63+
int join_multicast_group(const SocketAddress &address) { return NSAPI_ERROR_UNSUPPORTED; }
64+
6065
/** Connects TCP socket to a remote host
6166
*
6267
* Initiates a connection to a remote server specified by either

features/netsocket/UDPSocket.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ nsapi_protocol_t UDPSocket::get_proto()
3737
return NSAPI_UDP;
3838
}
3939

40+
4041
nsapi_size_or_error_t UDPSocket::sendto(const char *host, uint16_t port, const void *data, nsapi_size_t size)
4142
{
4243
SocketAddress address;

features/netsocket/nsapi_types.h

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ enum nsapi_error {
5353
NSAPI_ERROR_IS_CONNECTED = -3015, /*!< socket is already connected */
5454
NSAPI_ERROR_CONNECTION_LOST = -3016, /*!< connection lost */
5555
NSAPI_ERROR_CONNECTION_TIMEOUT = -3017, /*!< connection timed out */
56+
NSAPI_ERROR_ADDRESS_IN_USE = -3018, /*!< Address already in use */
5657
};
5758

5859
/** Type used to represent error codes
@@ -158,7 +159,7 @@ typedef void *nsapi_socket_t;
158159
/** Enum of socket protocols
159160
*
160161
* The socket protocol specifies a particular protocol to
161-
* be used with a newly created socket.
162+
* be used with a newly created socket.
162163
*
163164
* @enum nsapi_protocol
164165
*/
@@ -207,13 +208,15 @@ typedef enum nsapi_socket_level {
207208
* @enum nsapi_socket_option
208209
*/
209210
typedef enum nsapi_socket_option {
210-
NSAPI_REUSEADDR, /*!< Allow bind to reuse local addresses */
211-
NSAPI_KEEPALIVE, /*!< Enables sending of keepalive messages */
212-
NSAPI_KEEPIDLE, /*!< Sets timeout value to initiate keepalive */
213-
NSAPI_KEEPINTVL, /*!< Sets timeout value for keepalive */
214-
NSAPI_LINGER, /*!< Keeps close from returning until queues empty */
215-
NSAPI_SNDBUF, /*!< Sets send buffer size */
216-
NSAPI_RCVBUF, /*!< Sets recv buffer size */
211+
NSAPI_REUSEADDR, /*!< Allow bind to reuse local addresses */
212+
NSAPI_KEEPALIVE, /*!< Enables sending of keepalive messages */
213+
NSAPI_KEEPIDLE, /*!< Sets timeout value to initiate keepalive */
214+
NSAPI_KEEPINTVL, /*!< Sets timeout value for keepalive */
215+
NSAPI_LINGER, /*!< Keeps close from returning until queues empty */
216+
NSAPI_SNDBUF, /*!< Sets send buffer size */
217+
NSAPI_RCVBUF, /*!< Sets recv buffer size */
218+
NSAPI_ADD_MEMBERSHIP, /*!< Add membership to multicast address */
219+
NSAPI_DROP_MEMBERSHIP, /*!< Drop membership to multicast address */
217220
} nsapi_socket_option_t;
218221

219222
/** Supported IP protocol versions of IP stack
@@ -265,6 +268,13 @@ typedef struct nsapi_stack {
265268
unsigned _stack_buffer[16];
266269
} nsapi_stack_t;
267270

271+
/** nsapi_ip_mreq structure
272+
*/
273+
typedef struct nsapi_ip_mreq {
274+
nsapi_addr_t imr_multiaddr; /* IP multicast address of group */
275+
nsapi_addr_t imr_interface; /* local IP address of interface */
276+
} nsapi_ip_mreq_t;
277+
268278
/** nsapi_stack_api structure
269279
*
270280
* Common api structure for network stack operations. A network stack
@@ -286,9 +296,9 @@ typedef struct nsapi_stack_api
286296
*
287297
* The hostname may be either a domain name or an IP address. If the
288298
* hostname is an IP address, no network transactions will be performed.
289-
*
299+
*
290300
* If no stack-specific DNS resolution is provided, the hostname
291-
* will be resolve using a UDP socket on the stack.
301+
* will be resolve using a UDP socket on the stack.
292302
*
293303
* @param stack Stack handle
294304
* @param addr Destination for the host IP address
@@ -333,7 +343,7 @@ typedef struct nsapi_stack_api
333343
* @param optval Destination for option value
334344
* @param optlen Length of the option value
335345
* @return 0 on success, negative error code on failure
336-
*/
346+
*/
337347
nsapi_error_t (*getstackopt)(nsapi_stack_t *stack, int level,
338348
int optname, void *optval, unsigned *optlen);
339349

@@ -534,7 +544,7 @@ typedef struct nsapi_stack_api
534544
* @param optval Option value
535545
* @param optlen Length of the option value
536546
* @return 0 on success, negative error code on failure
537-
*/
547+
*/
538548
nsapi_error_t (*setsockopt)(nsapi_stack_t *stack, nsapi_socket_t socket, int level,
539549
int optname, const void *optval, unsigned optlen);
540550

@@ -551,7 +561,7 @@ typedef struct nsapi_stack_api
551561
* @param optval Destination for option value
552562
* @param optlen Length of the option value
553563
* @return 0 on success, negative error code on failure
554-
*/
564+
*/
555565
nsapi_error_t (*getsockopt)(nsapi_stack_t *stack, nsapi_socket_t socket, int level,
556566
int optname, void *optval, unsigned *optlen);
557567
} nsapi_stack_api_t;

0 commit comments

Comments
 (0)