-
Notifications
You must be signed in to change notification settings - Fork 3k
Add multicast implementation for UDPSocket #4820
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,7 @@ | |
#include "lwip/mld6.h" | ||
#include "lwip/dns.h" | ||
#include "lwip/udp.h" | ||
#include "lwip_errno.h" | ||
#include "netif/lwip_ethernet.h" | ||
#include "emac_api.h" | ||
#include "ppp_lwip.h" | ||
|
@@ -980,6 +981,25 @@ static nsapi_size_or_error_t mbed_lwip_socket_recvfrom(nsapi_stack_t *stack, nsa | |
return recv; | ||
} | ||
|
||
static nsapi_error_t nsapi_addr_to_ip_addr(ip_addr_t *lwip_ip, nsapi_addr_t *nsapi_addr_ip) { | ||
struct in_addr inet_addr_convert; | ||
// nsapi_addr_t stores IP address in byte array | ||
// Convert byte aray -> uint32_t for lwip inet IP representation | ||
ip_addr_t multiaddr_ip; | ||
if (!convert_mbed_addr_to_lwip(&multiaddr_ip, nsapi_addr_ip)) { | ||
return NSAPI_ERROR_PARAMETER; | ||
} | ||
|
||
inet_addr_convert.s_addr = multiaddr_ip.addr; | ||
|
||
#if LWIP_IPV4 | ||
// LWIP inet IP representation (in_addr) to ip_addr_t, both structs containing | ||
// uint32_t, this step just remaps the ip address for the igmp calls | ||
lwip_ip->addr = inet_addr_convert.s_addr; | ||
#endif | ||
return NSAPI_ERROR_OK; | ||
} | ||
|
||
static nsapi_error_t mbed_lwip_setsockopt(nsapi_stack_t *stack, nsapi_socket_t handle, int level, int optname, const void *optval, unsigned optlen) | ||
{ | ||
struct lwip_socket *s = (struct lwip_socket *)handle; | ||
|
@@ -1023,6 +1043,35 @@ static nsapi_error_t mbed_lwip_setsockopt(nsapi_stack_t *stack, nsapi_socket_t h | |
} | ||
return 0; | ||
|
||
case NSAPI_ADD_MEMBERSHIP: | ||
case NSAPI_DROP_MEMBERSHIP: { | ||
#if LWIP_IPV4 | ||
if (optlen != sizeof(nsapi_ip_mreq_t)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If your option structure contains And in that case you may as well include the code to call mld6_joingroup instead for IPv6. Having the logic to correctly detect and error when passed IPv6 addresses, and dealing with all 3 compilation modes (4 only, 6 only, 4+6) is no simpler than handling the IPv6. I'm currently thinking we should keep the mreq structure as is, so it contains nsapi_addr_t with the type field, and then you should do the right IPv4/IPv6 thing depending on the address type indicated in the group. |
||
return NSAPI_ERROR_UNSUPPORTED; | ||
} | ||
|
||
err_t igmp_err; | ||
nsapi_ip_mreq_t *imr = optval; | ||
|
||
ip_addr_t if_addr; | ||
ip_addr_t multi_addr; | ||
|
||
nsapi_addr_to_ip_addr(&multi_addr, &imr->imr_multiaddr); | ||
nsapi_addr_to_ip_addr(&if_addr, &imr->imr_interface); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For full IPv4/IPv6 correctness, I think you need to make sure that if multi_addr.version is NSAPI_UNSPEC, you produce an ip_addr_t of the correct type using ip_addr_set_zero_ip4 or ip_addr_set_zero_ip6. I think I'd restructure like:
Untested, but I think should cover all the compilation/runtime permutations of IPv4/IPv6. |
||
|
||
if (optname == NSAPI_ADD_MEMBERSHIP) { | ||
igmp_err = igmp_joingroup(&if_addr, &multi_addr); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't see how this compiles if LWIP_IPV6 is on as well as LWIP_IPV4 - you need to use ip_2_ip4 to convert the |
||
} else { | ||
igmp_err = igmp_leavegroup(&if_addr, &multi_addr); | ||
} | ||
if (igmp_err != ERR_OK) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would default to NSAPI_ERROR_DEVICE_ERROR. If that's fine I can change it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I read correctly, the only current possible error return is ERR_VAL, which maps NSAPI_ERROR_PARAMETER. |
||
return mbed_lwip_err_remap(EADDRNOTAVAIL); | ||
} | ||
return 0; | ||
#endif | ||
return NSAPI_ERROR_UNSUPPORTED; | ||
} | ||
|
||
default: | ||
return NSAPI_ERROR_UNSUPPORTED; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,27 @@ nsapi_protocol_t UDPSocket::get_proto() | |
return NSAPI_UDP; | ||
} | ||
|
||
int UDPSocket::modify_multicast_group(const SocketAddress &address, nsapi_socket_option_t socketopt) | ||
{ | ||
nsapi_ip_mreq_t mreq; | ||
|
||
// Set up group address | ||
mreq.imr_multiaddr = address.get_addr(); | ||
mreq.imr_interface = SocketAddress().get_addr(); // 0.0.0.0 INADDR_ANY | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Comment is misleading - it's an "NSAPI_UNSPEC" address, rather than IPv4 0.0.0.0. |
||
|
||
return this->setsockopt(0, socketopt, &mreq, sizeof(mreq)); | ||
} | ||
|
||
int UDPSocket::join_multicast_group(const SocketAddress &address) | ||
{ | ||
return modify_multicast_group(address, NSAPI_ADD_MEMBERSHIP); | ||
} | ||
|
||
int UDPSocket::leave_multicast_group(const SocketAddress &address) | ||
{ | ||
return modify_multicast_group(address, NSAPI_DROP_MEMBERSHIP); | ||
} | ||
|
||
nsapi_size_or_error_t UDPSocket::sendto(const char *host, uint16_t port, const void *data, nsapi_size_t size) | ||
{ | ||
SocketAddress address; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -158,7 +158,7 @@ typedef void *nsapi_socket_t; | |
/** Enum of socket protocols | ||
* | ||
* The socket protocol specifies a particular protocol to | ||
* be used with a newly created socket. | ||
* be used with a newly created socket. | ||
* | ||
* @enum nsapi_protocol | ||
*/ | ||
|
@@ -207,13 +207,15 @@ typedef enum nsapi_socket_level { | |
* @enum nsapi_socket_option | ||
*/ | ||
typedef enum nsapi_socket_option { | ||
NSAPI_REUSEADDR, /*!< Allow bind to reuse local addresses */ | ||
NSAPI_KEEPALIVE, /*!< Enables sending of keepalive messages */ | ||
NSAPI_KEEPIDLE, /*!< Sets timeout value to initiate keepalive */ | ||
NSAPI_KEEPINTVL, /*!< Sets timeout value for keepalive */ | ||
NSAPI_LINGER, /*!< Keeps close from returning until queues empty */ | ||
NSAPI_SNDBUF, /*!< Sets send buffer size */ | ||
NSAPI_RCVBUF, /*!< Sets recv buffer size */ | ||
NSAPI_REUSEADDR, /*!< Allow bind to reuse local addresses */ | ||
NSAPI_KEEPALIVE, /*!< Enables sending of keepalive messages */ | ||
NSAPI_KEEPIDLE, /*!< Sets timeout value to initiate keepalive */ | ||
NSAPI_KEEPINTVL, /*!< Sets timeout value for keepalive */ | ||
NSAPI_LINGER, /*!< Keeps close from returning until queues empty */ | ||
NSAPI_SNDBUF, /*!< Sets send buffer size */ | ||
NSAPI_RCVBUF, /*!< Sets recv buffer size */ | ||
NSAPI_ADD_MEMBERSHIP, /*!< Add membership to multicast address */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm beginning to hurt a little about the lack of prefixes or indeed proper levels here. The earlier ones should all have been Do we think we use a single socket option for both IPv4 and IPv6, as the nsapi_addr_t inside the mreq contains a version number? Is that your intent here? (Systems normally have separate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This PR is currently only for IPv4 support. This would be a good point for the IPv6 support PR. I can change it to IP_ if you feel strongly for it, but my preference is to keep new additions consistent with our current naming if we can't modify all of them to TCP_/IP_. |
||
NSAPI_DROP_MEMBERSHIP, /*!< Drop membership to multicast address */ | ||
} nsapi_socket_option_t; | ||
|
||
/* Backwards compatibility - previously didn't distinguish stack and socket options */ | ||
|
@@ -254,6 +256,13 @@ typedef struct nsapi_stack { | |
unsigned _stack_buffer[16]; | ||
} nsapi_stack_t; | ||
|
||
/** nsapi_ip_mreq structure | ||
*/ | ||
typedef struct nsapi_ip_mreq { | ||
nsapi_addr_t imr_multiaddr; /* IP multicast address of group */ | ||
nsapi_addr_t imr_interface; /* local IP address of interface */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The standard If this structure is intended for use by both IPv4 and IPv6, could we use an actual NetworkInterface * pointer instead (given that we don't have integer interface IDs)? Easier to get from the interface to the address if needed than the other way around. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This PR is aiming to add IPv4 support only. I can look into changing this to make the transition to IPv6 support easier if you'd rather get it down now but left as is for the moment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current implementation supports IPv4 and IPv6 equally. I don't think a PR that leaves IPv6 support less functional than IPv4 support is acceptable. Regardless, if you're adding IPv4 something you need to think what the IPv6 will look like. |
||
} nsapi_ip_mreq_t; | ||
|
||
/** nsapi_stack_api structure | ||
* | ||
* Common api structure for network stack operations. A network stack | ||
|
@@ -275,9 +284,9 @@ typedef struct nsapi_stack_api | |
* | ||
* The hostname may be either a domain name or an IP address. If the | ||
* hostname is an IP address, no network transactions will be performed. | ||
* | ||
* | ||
* If no stack-specific DNS resolution is provided, the hostname | ||
* will be resolve using a UDP socket on the stack. | ||
* will be resolve using a UDP socket on the stack. | ||
* | ||
* @param stack Stack handle | ||
* @param addr Destination for the host IP address | ||
|
@@ -322,7 +331,7 @@ typedef struct nsapi_stack_api | |
* @param optval Destination for option value | ||
* @param optlen Length of the option value | ||
* @return 0 on success, negative error code on failure | ||
*/ | ||
*/ | ||
nsapi_error_t (*getstackopt)(nsapi_stack_t *stack, int level, | ||
int optname, void *optval, unsigned *optlen); | ||
|
||
|
@@ -523,7 +532,7 @@ typedef struct nsapi_stack_api | |
* @param optval Option value | ||
* @param optlen Length of the option value | ||
* @return 0 on success, negative error code on failure | ||
*/ | ||
*/ | ||
nsapi_error_t (*setsockopt)(nsapi_stack_t *stack, nsapi_socket_t socket, int level, | ||
int optname, const void *optval, unsigned optlen); | ||
|
||
|
@@ -540,7 +549,7 @@ typedef struct nsapi_stack_api | |
* @param optval Destination for option value | ||
* @param optlen Length of the option value | ||
* @return 0 on success, negative error code on failure | ||
*/ | ||
*/ | ||
nsapi_error_t (*getsockopt)(nsapi_stack_t *stack, nsapi_socket_t socket, int level, | ||
int optname, void *optval, unsigned *optlen); | ||
} nsapi_stack_api_t; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't quite figure out what this function is doing, and why you're not just calling convert_mbed_addr_to_lwip directly. It's trying to fill in only part of the destination address structure? Is it covering up lack of proper IPv4/IPv6 support?