Merge branch 'feature/ipv6_socket_options' into 'master'
IPV6 multicast socket options See merge request !964
This commit is contained in:
commit
110c71d3f1
17 changed files with 1050 additions and 76 deletions
|
@ -35,11 +35,25 @@ config LWIP_THREAD_LOCAL_STORAGE_INDEX
|
|||
|
||||
config LWIP_SO_REUSE
|
||||
bool "Enable SO_REUSEADDR option"
|
||||
default n
|
||||
default y
|
||||
help
|
||||
Enabling this option allows binding to a port which remains in
|
||||
TIME_WAIT.
|
||||
|
||||
config LWIP_SO_REUSE_RXTOALL
|
||||
bool "SO_REUSEADDR copies broadcast/multicast to all matches"
|
||||
depends on LWIP_SO_REUSE
|
||||
default y
|
||||
help
|
||||
Enabling this option means that any incoming broadcast or multicast
|
||||
packet will be copied to all of the local sockets that it matches
|
||||
(may be more than one if SO_REUSEADDR is set on the socket.)
|
||||
|
||||
This increases memory overhead as the packets need to be copied,
|
||||
however they are only copied per matching socket. You can safely
|
||||
disable it if you don't plan to receive broadcast or multicast
|
||||
traffic on more than one socket at a time.
|
||||
|
||||
config LWIP_SO_RCVBUF
|
||||
bool "Enable SO_RCVBUF option"
|
||||
default n
|
||||
|
@ -88,7 +102,6 @@ config LWIP_ETHARP_TRUST_IP_MAC
|
|||
the peer is not already in the ARP table, adding a little latency.
|
||||
The peer *is* in the ARP table if it requested our address before.
|
||||
Also notice that this slows down input processing of every IP packet!
|
||||
|
||||
|
||||
config TCPIP_RECVMBOX_SIZE
|
||||
int "TCPIP receive mail box size"
|
||||
|
@ -98,6 +111,72 @@ config TCPIP_RECVMBOX_SIZE
|
|||
Set TCPIP receive mail box size. Generally bigger value means higher throughput
|
||||
but more memory. The value should be bigger than UDP/TCP mail box size.
|
||||
|
||||
config LWIP_DHCP_DOES_ARP_CHECK
|
||||
bool "DHCP: Perform ARP check on any offered address"
|
||||
default y
|
||||
help
|
||||
Enabling this option performs a check (via ARP request) if the offered IP address
|
||||
is not already in use by another host on the network.
|
||||
|
||||
menuconfig LWIP_AUTOIP
|
||||
bool "Enable IPV4 Link-Local Addressing (AUTOIP)"
|
||||
default n
|
||||
help
|
||||
Enabling this option allows the device to self-assign an address
|
||||
in the 169.256/16 range if none is assigned statically or via DHCP.
|
||||
|
||||
See RFC 3927.
|
||||
|
||||
config LWIP_AUTOIP_TRIES
|
||||
int "DHCP Probes before self-assigning IPv4 LL address"
|
||||
range 1 100
|
||||
default 2
|
||||
depends on LWIP_AUTOIP
|
||||
help
|
||||
DHCP client will send this many probes before self-assigning a
|
||||
link local address.
|
||||
|
||||
From LWIP help: "This can be set as low as 1 to get an AutoIP
|
||||
address very quickly, but you should be prepared to handle a
|
||||
changing IP address when DHCP overrides AutoIP." (In the case of
|
||||
ESP-IDF, this means multiple SYSTEM_EVENT_STA_GOT_IP events.)
|
||||
|
||||
config LWIP_AUTOIP_MAX_CONFLICTS
|
||||
int "Max IP conflicts before rate limiting"
|
||||
range 1 100
|
||||
default 9
|
||||
depends on LWIP_AUTOIP
|
||||
help
|
||||
If the AUTOIP functionality detects this many IP conflicts while
|
||||
self-assigning an address, it will go into a rate limited mode.
|
||||
|
||||
config LWIP_AUTOIP_RATE_LIMIT_INTERVAL
|
||||
int "Rate limited interval (seconds)"
|
||||
range 5 120
|
||||
default 20
|
||||
depends on LWIP_AUTOIP
|
||||
help
|
||||
If rate limiting self-assignment requests, wait this long between
|
||||
each request.
|
||||
|
||||
menuconfig LWIP_NETIF_LOOPBACK
|
||||
bool "Support per-interface loopback"
|
||||
default y
|
||||
help
|
||||
Enabling this option means that if a packet is sent with a destination
|
||||
address equal to the interface's own IP address, it will "loop back" and
|
||||
be received by this interface.
|
||||
|
||||
config LWIP_LOOPBACK_MAX_PBUFS
|
||||
int "Max queued loopback packets per interface"
|
||||
range 0 16
|
||||
default 8
|
||||
depends on LWIP_NETIF_LOOPBACK
|
||||
help
|
||||
Configure the maximum number of packets which can be queued for
|
||||
loopback on a given interface. Reducing this number may cause packets
|
||||
to be dropped, but will avoid filling memory with queued packet data.
|
||||
|
||||
menu "TCP"
|
||||
|
||||
config TCP_MAXRTX
|
||||
|
@ -234,13 +313,6 @@ config UDP_RECVMBOX_SIZE
|
|||
|
||||
endmenu # UDP
|
||||
|
||||
config LWIP_DHCP_DOES_ARP_CHECK
|
||||
bool "Enable an ARP check on the offered address"
|
||||
default y
|
||||
help
|
||||
Enabling this option allows check if the offered IP address is not already
|
||||
in use by another host on the network.
|
||||
|
||||
config TCPIP_TASK_STACK_SIZE
|
||||
int "TCP/IP Task Stack Size"
|
||||
default 2560
|
||||
|
|
|
@ -180,6 +180,16 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
|
|||
return;
|
||||
}
|
||||
|
||||
#if LWIP_IPV6
|
||||
/* This should be eventually moved to a flag on the UDP PCB, and this drop can happen
|
||||
more correctly in udp_input(). This will also allow icmp_dest_unreach() to be called. */
|
||||
if (conn->flags & NETCONN_FLAG_IPV6_V6ONLY && !ip_current_is_v6()) {
|
||||
LWIP_DEBUGF(API_MSG_DEBUG, ("recv_udp: Dropping IPv4 UDP packet (IPv6-only socket)"));
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
|
||||
if (buf == NULL) {
|
||||
pbuf_free(p);
|
||||
|
@ -1388,6 +1398,12 @@ lwip_netconn_do_send(void *m)
|
|||
|
||||
if (ERR_IS_FATAL(msg->conn->last_err)) {
|
||||
msg->err = msg->conn->last_err;
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
} else if ((msg->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) &&
|
||||
IP_IS_V4MAPPEDV6(&msg->msg.b->addr)) {
|
||||
LWIP_DEBUGF(API_MSG_DEBUG, ("lwip_netconn_do_send: Dropping IPv4 packet on IPv6-only socket"));
|
||||
msg->err = ERR_VAL;
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
} else {
|
||||
msg->err = ERR_CONN;
|
||||
if (msg->conn->pcb.tcp != NULL) {
|
||||
|
|
|
@ -261,7 +261,7 @@ lwip_freeaddrinfo(struct addrinfo *ai)
|
|||
* @param res pointer to a pointer where to store the result (set to NULL on failure)
|
||||
* @return 0 on success, non-zero on failure
|
||||
*
|
||||
* @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
|
||||
* @todo: implement AI_ADDRCONFIG
|
||||
*/
|
||||
int
|
||||
lwip_getaddrinfo(const char *nodename, const char *servname,
|
||||
|
@ -330,6 +330,9 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
|
|||
type = NETCONN_DNS_IPV4;
|
||||
} else if (ai_family == AF_INET6) {
|
||||
type = NETCONN_DNS_IPV6;
|
||||
if (hints->ai_flags & AI_V4MAPPED) {
|
||||
type = NETCONN_DNS_IPV6_IPV4;
|
||||
}
|
||||
}
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
err = netconn_gethostbyname_addrtype(nodename, &addr, type);
|
||||
|
@ -346,6 +349,14 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
|
|||
}
|
||||
}
|
||||
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
if (ai_family == AF_INET6 && (hints->ai_flags & AI_V4MAPPED)
|
||||
&& IP_GET_TYPE(&addr) == IPADDR_TYPE_V4) {
|
||||
/* Convert native V4 address to a V4-mapped IPV6 address */
|
||||
ip_addr_make_ip4_mapped_ip6(&addr, &addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
|
||||
if (nodename != NULL) {
|
||||
namelen = strlen(nodename);
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "lwip/api.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/raw.h"
|
||||
|
@ -364,21 +365,21 @@ union sockaddr_aligned {
|
|||
#define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
|
||||
#endif
|
||||
|
||||
/* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
|
||||
a socket is closed */
|
||||
/* This is to keep track of IP_ADD_MEMBERSHIP/IPV6_ADD_MEMBERSHIP calls to drop
|
||||
the membership when a socket is closed */
|
||||
struct lwip_socket_multicast_pair {
|
||||
/** the socket (+1 to not require initialization) */
|
||||
int sa;
|
||||
/** the interface address */
|
||||
ip4_addr_t if_addr;
|
||||
ip_addr_t if_addr;
|
||||
/** the group address */
|
||||
ip4_addr_t multi_addr;
|
||||
ip_addr_t multi_addr;
|
||||
};
|
||||
|
||||
struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
|
||||
struct lwip_socket_multicast_pair socket_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
|
||||
|
||||
static int lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
|
||||
static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
|
||||
static int lwip_socket_register_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr);
|
||||
static void lwip_socket_unregister_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr);
|
||||
static void lwip_socket_drop_registered_memberships(int s);
|
||||
#endif /* LWIP_IGMP */
|
||||
|
||||
|
@ -852,7 +853,7 @@ lwip_close(int s)
|
|||
LWIP_ASSERT("lwip_close: sock->lastdata == NULL", sock->lastdata == NULL);
|
||||
}
|
||||
|
||||
#if LWIP_IGMP
|
||||
#if (LWIP_IGMP) || (LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS)
|
||||
/* drop all possibly joined IGMP memberships */
|
||||
lwip_socket_drop_registered_memberships(s);
|
||||
#endif /* LWIP_IGMP */
|
||||
|
@ -2372,10 +2373,6 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
|
|||
switch (optname) {
|
||||
case IPV6_V6ONLY:
|
||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
|
||||
/* @todo: this does not work for datagram sockets, yet */
|
||||
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
|
||||
return ENOPROTOOPT;
|
||||
}
|
||||
*(int*)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
|
||||
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
|
||||
s, *(int *)optval));
|
||||
|
@ -2387,6 +2384,38 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
|
|||
break;
|
||||
} /* switch (optname) */
|
||||
break;
|
||||
#if LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS /* Multicast options, similar to LWIP_IGMP options for IPV4 */
|
||||
case IPV6_MULTICAST_IF: /* NB: like IP_MULTICAST_IF, this returns an IP not an index */
|
||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in6_addr);
|
||||
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
|
||||
return ENOPROTOOPT;
|
||||
}
|
||||
inet6_addr_from_ip6addr((struct in6_addr*)optval,
|
||||
udp_get_multicast_netif_ip6addr(sock->conn->pcb.udp));
|
||||
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_IF) = 0x%"X32_F"\n",
|
||||
s, *(u32_t *)optval));
|
||||
break;
|
||||
case IPV6_MULTICAST_HOPS:
|
||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
|
||||
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
|
||||
return ENOPROTOOPT;
|
||||
}
|
||||
*(u8_t*)optval = sock->conn->pcb.udp->mcast_ttl;
|
||||
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IP_MULTICAST_LOOP) = %d\n",
|
||||
s, *(int *)optval));
|
||||
break;
|
||||
case IPV6_MULTICAST_LOOP:
|
||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
|
||||
if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
|
||||
*(u8_t*)optval = 1;
|
||||
} else {
|
||||
*(u8_t*)optval = 0;
|
||||
}
|
||||
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IP_MULTICAST_LOOP) = %d\n",
|
||||
s, *(int *)optval));
|
||||
break;
|
||||
|
||||
#endif /* LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS */
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
#if LWIP_UDP && LWIP_UDPLITE
|
||||
|
@ -2685,21 +2714,21 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
|
|||
/* @todo: assign membership to this socket so that it is dropped when state the socket */
|
||||
err_t igmp_err;
|
||||
const struct ip_mreq *imr = (const struct ip_mreq *)optval;
|
||||
ip4_addr_t if_addr;
|
||||
ip4_addr_t multi_addr;
|
||||
ip_addr_t if_addr;
|
||||
ip_addr_t multi_addr;
|
||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
|
||||
inet_addr_to_ipaddr(&if_addr, &imr->imr_interface);
|
||||
inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr);
|
||||
inet_addr_to_ipaddr(ip_2_ip4(&if_addr), &imr->imr_interface);
|
||||
inet_addr_to_ipaddr(ip_2_ip4(&multi_addr), &imr->imr_multiaddr);
|
||||
if (optname == IP_ADD_MEMBERSHIP) {
|
||||
if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
|
||||
/* cannot track membership (out of memory) */
|
||||
err = ENOMEM;
|
||||
igmp_err = ERR_OK;
|
||||
} else {
|
||||
igmp_err = igmp_joingroup(&if_addr, &multi_addr);
|
||||
igmp_err = igmp_joingroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr));
|
||||
}
|
||||
} else {
|
||||
igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
|
||||
igmp_err = igmp_leavegroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr));
|
||||
lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
|
||||
}
|
||||
if (igmp_err != ERR_OK) {
|
||||
|
@ -2778,12 +2807,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
|
|||
case IPPROTO_IPV6:
|
||||
switch (optname) {
|
||||
case IPV6_V6ONLY:
|
||||
/* @todo: this does not work for datagram sockets, yet */
|
||||
#if CONFIG_MDNS
|
||||
//LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
|
||||
#else
|
||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
|
||||
#endif
|
||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
|
||||
if (*(const int*)optval) {
|
||||
netconn_set_ipv6only(sock->conn, 1);
|
||||
} else {
|
||||
|
@ -2792,6 +2816,55 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
|
|||
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
|
||||
s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
|
||||
break;
|
||||
#if LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS /* Multicast options, similar to LWIP_IGMP options for IPV4 */
|
||||
case IPV6_MULTICAST_IF: /* NB: like IP_MULTICAST_IF, this takes an IP not an index */
|
||||
{
|
||||
ip6_addr_t if_addr;
|
||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in6_addr, NETCONN_UDP);
|
||||
inet6_addr_to_ip6addr(&if_addr, (const struct in6_addr*)optval);
|
||||
udp_set_multicast_netif_ip6addr(sock->conn->pcb.udp, &if_addr);
|
||||
}
|
||||
break;
|
||||
case IPV6_MULTICAST_HOPS:
|
||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
|
||||
sock->conn->pcb.udp->mcast_ttl = (u8_t)(*(const u8_t*)optval);
|
||||
break;
|
||||
case IPV6_MULTICAST_LOOP:
|
||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
|
||||
if (*(const u8_t*)optval) {
|
||||
udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
|
||||
} else {
|
||||
udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
|
||||
}
|
||||
break;
|
||||
case IPV6_ADD_MEMBERSHIP:
|
||||
case IPV6_DROP_MEMBERSHIP:
|
||||
{
|
||||
err_t mld_err;
|
||||
const struct ip6_mreq *imr = (const struct ip6_mreq *)optval;
|
||||
ip_addr_t if_addr;
|
||||
ip_addr_t multi_addr;
|
||||
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip6_mreq, NETCONN_UDP);
|
||||
inet6_addr_to_ip6addr(ip_2_ip6(&if_addr), &imr->ipv6mr_interface);
|
||||
inet6_addr_to_ip6addr(ip_2_ip6(&multi_addr), &imr->ipv6mr_multiaddr);
|
||||
if (optname == IPV6_ADD_MEMBERSHIP) {
|
||||
if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
|
||||
/* cannot track membership (out of memory) */
|
||||
err = ENOMEM;
|
||||
mld_err = ERR_OK;
|
||||
} else {
|
||||
mld_err = mld6_joingroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr));
|
||||
}
|
||||
} else {
|
||||
mld_err = mld6_leavegroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr));
|
||||
lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
|
||||
}
|
||||
if (mld_err != ERR_OK) {
|
||||
err = EADDRNOTAVAIL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif /* LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS */
|
||||
default:
|
||||
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
|
||||
s, optname));
|
||||
|
@ -3005,15 +3078,15 @@ lwip_fcntl(int s, int cmd, int val)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if LWIP_IGMP
|
||||
/** Register a new IGMP membership. On socket close, the membership is dropped automatically.
|
||||
#if LWIP_IGMP || (LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS)
|
||||
/** Register a new multicast group membership. On socket close, the membership is dropped automatically.
|
||||
*
|
||||
* ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
|
||||
*
|
||||
* @return 1 on success, 0 on failure
|
||||
*/
|
||||
static int
|
||||
lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
|
||||
lwip_socket_register_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr)
|
||||
{
|
||||
/* s+1 is stored in the array to prevent having to initialize the array
|
||||
(default initialization is to 0) */
|
||||
|
@ -3021,10 +3094,10 @@ lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr
|
|||
int i;
|
||||
|
||||
for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
|
||||
if (socket_ipv4_multicast_memberships[i].sa == 0) {
|
||||
socket_ipv4_multicast_memberships[i].sa = sa;
|
||||
ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
|
||||
ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
|
||||
if (socket_multicast_memberships[i].sa == 0) {
|
||||
socket_multicast_memberships[i].sa = sa;
|
||||
ip_addr_copy(socket_multicast_memberships[i].if_addr, *if_addr);
|
||||
ip_addr_copy(socket_multicast_memberships[i].multi_addr, *multi_addr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -3037,7 +3110,7 @@ lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr
|
|||
* ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
|
||||
*/
|
||||
static void
|
||||
lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
|
||||
lwip_socket_unregister_membership(int s, const ip_addr_t *if_addr, const ip_addr_t *multi_addr)
|
||||
{
|
||||
/* s+1 is stored in the array to prevent having to initialize the array
|
||||
(default initialization is to 0) */
|
||||
|
@ -3045,12 +3118,12 @@ lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_ad
|
|||
int i;
|
||||
|
||||
for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
|
||||
if ((socket_ipv4_multicast_memberships[i].sa == sa) &&
|
||||
ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) &&
|
||||
ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) {
|
||||
socket_ipv4_multicast_memberships[i].sa = 0;
|
||||
ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
|
||||
ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
|
||||
if ((socket_multicast_memberships[i].sa == sa) &&
|
||||
ip_addr_cmp(&socket_multicast_memberships[i].if_addr, if_addr) &&
|
||||
ip_addr_cmp(&socket_multicast_memberships[i].multi_addr, multi_addr)) {
|
||||
socket_multicast_memberships[i].sa = 0;
|
||||
ip_addr_set_zero(&socket_multicast_memberships[i].if_addr);
|
||||
ip_addr_set_zero(&socket_multicast_memberships[i].multi_addr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -3070,19 +3143,18 @@ static void lwip_socket_drop_registered_memberships(int s)
|
|||
LWIP_ASSERT("socket has no netconn", sockets[s].conn != NULL);
|
||||
|
||||
for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
|
||||
if (socket_ipv4_multicast_memberships[i].sa == sa) {
|
||||
ip_addr_t multi_addr, if_addr;
|
||||
ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr);
|
||||
ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr);
|
||||
socket_ipv4_multicast_memberships[i].sa = 0;
|
||||
ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
|
||||
ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
|
||||
|
||||
netconn_join_leave_group(sockets[s].conn, &multi_addr, &if_addr, NETCONN_LEAVE);
|
||||
if (socket_multicast_memberships[i].sa == sa) {
|
||||
socket_multicast_memberships[i].sa = 0;
|
||||
netconn_join_leave_group(sockets[s].conn,
|
||||
&socket_multicast_memberships[i].multi_addr,
|
||||
&socket_multicast_memberships[i].if_addr,
|
||||
NETCONN_LEAVE);
|
||||
ip_addr_set_zero(&socket_multicast_memberships[i].if_addr);
|
||||
ip_addr_set_zero(&socket_multicast_memberships[i].multi_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* LWIP_IGMP */
|
||||
#endif /* LWIP_IGMP || (LWIP_IPV6_MLD && LWIP_MULTICAST_TX_OPTIONS) */
|
||||
|
||||
#if ESP_THREAD_SAFE
|
||||
|
||||
|
|
|
@ -882,6 +882,11 @@ ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
|
|||
}
|
||||
}
|
||||
}
|
||||
#if LWIP_IPV6_MLD
|
||||
if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
|
||||
netif_loop_output(netif, p);
|
||||
}
|
||||
#endif /* LWIP_IPV6_MLD */
|
||||
#endif /* ENABLE_LOOPBACK */
|
||||
#if LWIP_IPV6_FRAG
|
||||
/* don't fragment if interface has mtu set to 0 [loopif] */
|
||||
|
|
|
@ -542,6 +542,19 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
|
|||
|
||||
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n"));
|
||||
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
/* Unwrap IPV4-mapped IPV6 addresses and convert to native IPV4 here */
|
||||
if (IP_IS_V4MAPPEDV6(dst_ip)) {
|
||||
ip_addr_t dest_ipv4;
|
||||
ip_addr_ip4_from_mapped_ip6(&dest_ipv4, dst_ip);
|
||||
#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UP
|
||||
return udp_sendto_chksum(pcb, p, &dest_ipv4, dst_port, have_chksum, chksum);
|
||||
#else
|
||||
return udp_sendto(pcb, p, &dest_ipv4, dst_port);
|
||||
#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UP */
|
||||
}
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
|
||||
#if LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS)
|
||||
if (ip_addr_ismulticast(dst_ip_route)) {
|
||||
#if LWIP_IPV6
|
||||
|
|
|
@ -68,13 +68,8 @@ extern "C" {
|
|||
#define ANNOUNCE_NUM 2 /* (number of announcement packets) */
|
||||
#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */
|
||||
#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */
|
||||
#if CONFIG_MDNS
|
||||
#define MAX_CONFLICTS 9 /* (max conflicts before rate limiting) */
|
||||
#define RATE_LIMIT_INTERVAL 20 /* seconds (delay between successive attempts) */
|
||||
#else
|
||||
#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */
|
||||
#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */
|
||||
#endif
|
||||
#define MAX_CONFLICTS LWIP_AUTOIP_MAX_CONFLICTS /* (max conflicts before rate limiting) */
|
||||
#define RATE_LIMIT_INTERVAL LWIP_AUTOIP_RATE_LIMIT_INTERVAL /* seconds (delay between successive attempts) */
|
||||
#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */
|
||||
|
||||
/* AutoIP client states */
|
||||
|
|
|
@ -69,6 +69,10 @@ extern const ip_addr_t ip_addr_any_type;
|
|||
|
||||
#define IP_IS_V6_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V6)
|
||||
#define IP_IS_V6(ipaddr) (((ipaddr) != NULL) && IP_IS_V6_VAL(*(ipaddr)))
|
||||
|
||||
#define IP_V6_EQ_PART(ipaddr, WORD, VAL) (ip_2_ip6(ipaddr)->addr[WORD] == htonl(VAL))
|
||||
#define IP_IS_V4MAPPEDV6(ipaddr) (IP_IS_V6(ipaddr) && IP_V6_EQ_PART(ipaddr, 0, 0) && IP_V6_EQ_PART(ipaddr, 1, 0) && IP_V6_EQ_PART(ipaddr, 2, 0x0000FFFF))
|
||||
|
||||
#define IP_SET_TYPE_VAL(ipaddr, iptype) do { (ipaddr).type = (iptype); }while(0)
|
||||
#define IP_SET_TYPE(ipaddr, iptype) do { if((ipaddr) != NULL) { IP_SET_TYPE_VAL(*(ipaddr), iptype); }}while(0)
|
||||
#define IP_GET_TYPE(ipaddr) ((ipaddr)->type)
|
||||
|
@ -156,6 +160,27 @@ extern const ip_addr_t ip_addr_any_type;
|
|||
((IP_IS_V6(addr)) ? ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen) : ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen)))
|
||||
int ipaddr_aton(const char *cp, ip_addr_t *addr);
|
||||
|
||||
/* Map an IPv4 ip_addr into an IPV6 ip_addr, using format
|
||||
defined in RFC4291 2.5.5.2.
|
||||
|
||||
Safe to call when dest==src.
|
||||
*/
|
||||
#define ip_addr_make_ip4_mapped_ip6(dest, src) do { \
|
||||
u32_t tmp = ip_2_ip4(src)->addr; \
|
||||
IP_ADDR6((dest), 0x0, 0x0, htonl(0x0000FFFF), tmp); \
|
||||
} while(0)
|
||||
|
||||
/* Convert an IPv4 mapped V6 address to an IPV4 address.
|
||||
|
||||
Check IP_IS_V4MAPPEDV6(src) before using this.
|
||||
|
||||
Safe to call when dest == src.
|
||||
*/
|
||||
#define ip_addr_ip4_from_mapped_ip6(dest, src) do { \
|
||||
ip_2_ip4(dest)->addr = ip_2_ip6(src)->addr[3]; \
|
||||
IP_SET_TYPE(dest, IPADDR_TYPE_V4); \
|
||||
} while(0)
|
||||
|
||||
#else /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
|
||||
#define IP_ADDR_PCB_VERSION_MATCH(addr, pcb) 1
|
||||
|
|
|
@ -823,6 +823,22 @@
|
|||
#define LWIP_DHCP_AUTOIP_COOP_TRIES 9
|
||||
#endif
|
||||
|
||||
/**
|
||||
* LWIP_AUTOIP_MAX_CONFLICTS:
|
||||
* Maximum number of AutoIP IP conflicts before rate limiting is enabled.
|
||||
*/
|
||||
#ifndef LWIP_AUTOIP_MAX_CONFLICTS
|
||||
#define LWIP_AUTOIP_MAX_CONFLICTS 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* LWIP_AUTOIP_RATE_LIMIT_INTERVAL:
|
||||
* Rate limited request interval, in seconds.
|
||||
*/
|
||||
#ifndef LWIP_AUTOIP_RATE_LIMIT_INTERVAL
|
||||
#define LWIP_AUTOIP_RATE_LIMIT_INTERVAL 60
|
||||
#endif
|
||||
|
||||
/*
|
||||
----------------------------------
|
||||
----- SNMP MIB2 support -----
|
||||
|
|
|
@ -262,6 +262,27 @@ struct linger {
|
|||
*/
|
||||
#define IPV6_CHECKSUM 7 /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */
|
||||
#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */
|
||||
|
||||
#if LWIP_IPV6_MLD
|
||||
/* Socket options for IPV6 multicast, uses the MLD interface to manage group memberships. RFC2133. */
|
||||
#define IPV6_MULTICAST_IF 0x300
|
||||
#define IPV6_MULTICAST_HOPS 0x301
|
||||
#define IPV6_MULTICAST_LOOP 0x302
|
||||
#define IPV6_ADD_MEMBERSHIP 0x303
|
||||
#define IPV6_DROP_MEMBERSHIP 0x304
|
||||
|
||||
/* Structure used for IPV6_ADD/DROP_MEMBERSHIP */
|
||||
typedef struct ip6_mreq {
|
||||
struct in6_addr ipv6mr_multiaddr; /* IPv6 multicast addr */
|
||||
struct in6_addr ipv6mr_interface; /* local IP address of interface */
|
||||
} ip6_mreq;
|
||||
|
||||
/* Commonly used synonyms for these options */
|
||||
#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
|
||||
#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
|
||||
|
||||
#endif /* LWIP_IPV6_MLD */
|
||||
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
#if LWIP_UDP && LWIP_UDPLITE
|
||||
|
|
|
@ -173,6 +173,12 @@ void udp_init (void);
|
|||
#define udp_get_multicast_netif_addr(pcb) ip_2_ip4(&(pcb)->multicast_ip)
|
||||
#define udp_set_multicast_ttl(pcb, value) do { (pcb)->mcast_ttl = value; } while(0)
|
||||
#define udp_get_multicast_ttl(pcb) ((pcb)->mcast_ttl)
|
||||
|
||||
#if LWIP_IPV6_MLD
|
||||
#define udp_set_multicast_netif_ip6addr(pcb, ip6addr) ip_addr_copy_from_ip6((pcb)->multicast_ip, *(ip6addr))
|
||||
#define udp_get_multicast_netif_ip6addr(pcb) ip_2_ip6(&(pcb)->multicast_ip)
|
||||
#endif
|
||||
|
||||
#endif /* LWIP_MULTICAST_TX_OPTIONS */
|
||||
|
||||
#if UDP_DEBUG
|
||||
|
|
|
@ -221,10 +221,7 @@
|
|||
---------- AUTOIP options ----------
|
||||
------------------------------------
|
||||
*/
|
||||
#if CONFIG_MDNS
|
||||
/**
|
||||
* LWIP_AUTOIP==1: Enable AUTOIP module.
|
||||
*/
|
||||
#ifdef CONFIG_LWIP_AUTOIP
|
||||
#define LWIP_AUTOIP 1
|
||||
|
||||
/**
|
||||
|
@ -240,8 +237,13 @@
|
|||
* be prepared to handle a changing IP address when DHCP overrides
|
||||
* AutoIP.
|
||||
*/
|
||||
#define LWIP_DHCP_AUTOIP_COOP_TRIES 2
|
||||
#endif
|
||||
#define LWIP_DHCP_AUTOIP_COOP_TRIES CONFIG_LWIP_AUTOIP_TRIES
|
||||
|
||||
#define LWIP_AUTOIP_MAX_CONFLICTS CONFIG_LWIP_AUTOIP_MAX_CONFLICTS
|
||||
|
||||
#define LWIP_AUTOIP_RATE_LIMIT_INTERVAL CONFIG_LWIP_AUTOIP_RATE_LIMIT_INTERVAL
|
||||
|
||||
#endif /* CONFIG_LWIP_AUTOIP */
|
||||
|
||||
/*
|
||||
----------------------------------
|
||||
|
@ -367,7 +369,7 @@
|
|||
---------- LOOPIF options ----------
|
||||
------------------------------------
|
||||
*/
|
||||
#if CONFIG_MDNS
|
||||
#ifdef CONFIG_LWIP_NETIF_LOOPBACK
|
||||
/**
|
||||
* LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP
|
||||
* address equal to the netif IP address, looping them back up the stack.
|
||||
|
@ -378,7 +380,7 @@
|
|||
* LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback
|
||||
* sending for each netif (0 = disabled)
|
||||
*/
|
||||
#define LWIP_LOOPBACK_MAX_PBUFS 8
|
||||
#define LWIP_LOOPBACK_MAX_PBUFS CONFIG_LWIP_LOOPBACK_MAX_PBUFS
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -506,14 +508,12 @@
|
|||
*/
|
||||
#define SO_REUSE CONFIG_LWIP_SO_REUSE
|
||||
|
||||
#if CONFIG_MDNS
|
||||
/**
|
||||
* SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets
|
||||
* to all local matches if SO_REUSEADDR is turned on.
|
||||
* WARNING: Adds a memcpy for every packet if passing to more than one pcb!
|
||||
*/
|
||||
#define SO_REUSE_RXTOALL 1
|
||||
#endif
|
||||
#define SO_REUSE_RXTOALL CONFIG_LWIP_SO_REUSE_RXTOALL
|
||||
|
||||
/*
|
||||
----------------------------------------
|
||||
|
|
9
examples/protocols/udp_multicast/Makefile
Normal file
9
examples/protocols/udp_multicast/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := udp-multicast
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
66
examples/protocols/udp_multicast/README.md
Normal file
66
examples/protocols/udp_multicast/README.md
Normal file
|
@ -0,0 +1,66 @@
|
|||
# UDP Multicast Example
|
||||
|
||||
This example shows how to use the IPV4 & IPV6 UDP multicast features via the BSD-style sockets interface.
|
||||
|
||||
## Behaviour
|
||||
|
||||
The behaviour of the example is:
|
||||
|
||||
* Listens to specified multicast addresses (one IPV4 and/or one IPV6).
|
||||
* Print any UDP packets received as ASCII text.
|
||||
* If no packets are received it will periodicially (after 2.5 seconds) send its own plaintext packet(s) to the multicast address(es).
|
||||
|
||||
## Configuration
|
||||
|
||||
The "Example Configuration" menu "make menuconfig" allows you to configure the details of the example:
|
||||
|
||||
* WiFi SSD & Password
|
||||
* IP Mode: IPV4 & IPV6 dual, IPV4 only, or IPv6 only.
|
||||
* Multicast addresses for IPV4 and/or IPV6.
|
||||
* Enable multicast socket loopback (ie should the socket receive its own multicast transmissions.)
|
||||
* Change the interface to add the multicast group on (default interface, or WiFi STA interface.) Both methods are valid.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
In IPV4 & IPV6 dual mode, an IPV6 socket is created and the "dual mode" options described in [RFC4038](https://tools.ietf.org/html/rfc4038) are used to bind it to the default address for both IPV4 & IPV6 and join both the configured IPV4 & IPV6 multicast groups. Otherwise, a single socket of the appropriate type is created.
|
||||
|
||||
The socket is always bound to the default address, so it will also receive unicast packets. If you only want to receive multicast packets for a particular address, `bind()` to that multicast address instead.
|
||||
|
||||
## Host Tools
|
||||
|
||||
There are many host-side tools which can be used to interact with the UDP multicast example. One command line tool is [socat](http://www.dest-unreach.org/socat/) which can send and receive many kinds of packets.
|
||||
|
||||
### Send IPV4 multicast via socat
|
||||
|
||||
```
|
||||
echo "Hi there, IPv4!" | socat STDIO UDP4-DATAGRAM:232.10.11.12:3333,ip-multicast-if=(host_ip_addr)
|
||||
```
|
||||
|
||||
Replace `232.10.11.12:3333` with the IPV4 multicast address and port, and `(host_ip_addr)` with the host's IP address (used to find the interface to send the multicast packet on.)
|
||||
|
||||
### Receive IPV4 multicast via socat
|
||||
|
||||
```
|
||||
socat STDIO UDP4-RECVFROM:3333,ip-add-membership=232.10.11.12:(host_ip_addr)
|
||||
```
|
||||
|
||||
Replace `:3333` and `232.10.11.12` with the port and IPV4 multicast address, respectively. Replace `(host_ip_addr)` with the host IP address, used to find the interface to listen on.
|
||||
|
||||
(The `,ip-add-membership=...` clause may not be necessary, depending on your network configuration.)
|
||||
|
||||
### Send IPV6 multicast via socat
|
||||
|
||||
```
|
||||
echo "Hi there, IPV6!" | socat STDIO UDP6-DATAGRAM:[ff02::fc]:3333
|
||||
```
|
||||
|
||||
Replace `[ff02::fc]:3333` with the IPV6 multicast address and port, respectively.
|
||||
|
||||
### Receive IPV6 multicast via socat
|
||||
|
||||
At time of writing this is not possible without patching socat. Use a different tool or programming language to receive IPV6 multicast packets.
|
||||
|
||||
## About Examples
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for general information about examples.
|
||||
|
91
examples/protocols/udp_multicast/main/Kconfig.projbuild
Normal file
91
examples/protocols/udp_multicast/main/Kconfig.projbuild
Normal file
|
@ -0,0 +1,91 @@
|
|||
menu "Example Configuration"
|
||||
|
||||
config WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "myssid"
|
||||
help
|
||||
SSID (network name) for the example to connect to.
|
||||
|
||||
config WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "myssid"
|
||||
help
|
||||
WiFi password (WPA or WPA2) for the example to use.
|
||||
|
||||
Can be left blank if the network has no security set.
|
||||
|
||||
choice EXAMPLE_IP_MODE
|
||||
prompt "Multicast IP type"
|
||||
help
|
||||
Example can multicast IPV4, IPV6, or both.
|
||||
|
||||
config EXAMPLE_IPV4_V6
|
||||
bool "IPV4 & IPV6"
|
||||
select EXAMPLE_IPV4
|
||||
select EXAMPLE_IPV6
|
||||
|
||||
config EXAMPLE_IPV4_ONLY
|
||||
bool "IPV4"
|
||||
select EXAMPLE_IPV4
|
||||
|
||||
config EXAMPLE_IPV6_ONLY
|
||||
bool "IPV6"
|
||||
select EXAMPLE_IPV6
|
||||
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_IPV4
|
||||
bool
|
||||
config EXAMPLE_IPV6
|
||||
bool
|
||||
|
||||
config EXAMPLE_MULTICAST_IPV4_ADDR
|
||||
string "Multicast IPV4 Address (send & receive)"
|
||||
default "232.10.11.12"
|
||||
depends on EXAMPLE_IPV4
|
||||
help
|
||||
IPV4 multicast address. Example will both send to and listen to this address.
|
||||
|
||||
config EXAMPLE_MULTICAST_IPV6_ADDR
|
||||
string "Multicast IPV6 Address (send & receive)"
|
||||
default "FF02::FC"
|
||||
depends on EXAMPLE_IPV6
|
||||
help
|
||||
IPV6 multicast address. Example will both send to and listen to this address.
|
||||
|
||||
The default FF02::FC address is a link-local multicast address. Consult IPV6 specifications or documentation for information about meaning of different IPV6 multicast ranges.
|
||||
|
||||
config EXAMPLE_PORT
|
||||
int "Multicast port (send & receive)"
|
||||
range 0 65535
|
||||
default 333
|
||||
help
|
||||
Multicast port the example will both send & receive UDP packets on.
|
||||
|
||||
config EXAMPLE_LOOPBACK
|
||||
bool "Multicast loopback"
|
||||
help
|
||||
Enables IP_MULTICAST_LOOP/IPV6_MULTICAST_LOOP options, meaning
|
||||
that packets transmitted from the device are also received by the
|
||||
device itself.
|
||||
|
||||
config EXAMPLE_MULTICAST_TTL
|
||||
int "Multicast packet TTL"
|
||||
range 1 255
|
||||
help
|
||||
Sets TTL field of multicast packets. Separate from uni- & broadcast TTL.
|
||||
|
||||
choice EXAMPLE_MULTICAST_IF
|
||||
prompt "Multicast Interface"
|
||||
help
|
||||
Multicast socket can bind to default interface, or all interfaces.
|
||||
|
||||
config EXAMPLE_MULTICAST_LISTEN_DEFAULT_IF
|
||||
bool "Default interface"
|
||||
|
||||
config EXAMPLE_MULTICAST_LISTEN_STA_IF
|
||||
bool "WiFi STA interface"
|
||||
|
||||
endchoice
|
||||
|
||||
endmenu
|
4
examples/protocols/udp_multicast/main/component.mk
Normal file
4
examples/protocols/udp_multicast/main/component.mk
Normal file
|
@ -0,0 +1,4 @@
|
|||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
|
@ -0,0 +1,552 @@
|
|||
/* UDP MultiCast Send/Receive Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event_loop.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/sys.h"
|
||||
#include <lwip/netdb.h>
|
||||
|
||||
/* The examples use simple WiFi configuration that you can set via
|
||||
'make menuconfig'.
|
||||
|
||||
If you'd rather not, just change the below entries to strings with
|
||||
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
|
||||
*/
|
||||
#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
|
||||
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
|
||||
|
||||
#define UDP_PORT CONFIG_EXAMPLE_PORT
|
||||
|
||||
#define MULTICAST_LOOPBACK CONFIG_EXAMPLE_LOOPBACK
|
||||
|
||||
#define MULTICAST_TTL CONFIG_EXAMPLE_MULTICAST_TTL
|
||||
|
||||
#define MULTICAST_IPV4_ADDR CONFIG_EXAMPLE_MULTICAST_IPV4_ADDR
|
||||
#define MULTICAST_IPV6_ADDR CONFIG_EXAMPLE_MULTICAST_IPV6_ADDR
|
||||
|
||||
#define LISTEN_DEFAULT_IF CONFIG_EXAMPLE_LISTEN_DEFAULT_IF
|
||||
|
||||
/* FreeRTOS event group to signal when we are connected & ready to make a request */
|
||||
static EventGroupHandle_t wifi_event_group;
|
||||
|
||||
/* The event group allows multiple bits for each event,
|
||||
we use two - one for IPv4 "got ip", and
|
||||
one for IPv6 "got ip". */
|
||||
const int IPV4_GOTIP_BIT = BIT0;
|
||||
const int IPV6_GOTIP_BIT = BIT1;
|
||||
|
||||
static const char *TAG = "multicast";
|
||||
#ifdef CONFIG_EXAMPLE_IPV4
|
||||
static const char *V4TAG = "mcast-ipv4";
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_IPV6
|
||||
static const char *V6TAG = "mcast-ipv6";
|
||||
#endif
|
||||
|
||||
static esp_err_t event_handler(void *ctx, system_event_t *event)
|
||||
{
|
||||
switch(event->event_id) {
|
||||
case SYSTEM_EVENT_STA_START:
|
||||
esp_wifi_connect();
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_CONNECTED:
|
||||
/* enable ipv6 */
|
||||
tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
xEventGroupSetBits(wifi_event_group, IPV4_GOTIP_BIT);
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
/* This is a workaround as ESP32 WiFi libs don't currently
|
||||
auto-reassociate. */
|
||||
esp_wifi_connect();
|
||||
xEventGroupClearBits(wifi_event_group, IPV4_GOTIP_BIT);
|
||||
xEventGroupClearBits(wifi_event_group, IPV6_GOTIP_BIT);
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_STA_GOT_IP6:
|
||||
xEventGroupSetBits(wifi_event_group, IPV6_GOTIP_BIT);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void initialise_wifi(void)
|
||||
{
|
||||
tcpip_adapter_init();
|
||||
wifi_event_group = xEventGroupCreate();
|
||||
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
.ssid = EXAMPLE_WIFI_SSID,
|
||||
.password = EXAMPLE_WIFI_PASS,
|
||||
},
|
||||
};
|
||||
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
|
||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
|
||||
ESP_ERROR_CHECK( esp_wifi_start() );
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV4
|
||||
/* Add a socket, either IPV4-only or IPV6 dual mode, to the IPV4
|
||||
multicast group */
|
||||
static int socket_add_ipv4_multicast_group(int sock, bool assign_source_if)
|
||||
{
|
||||
struct ip_mreq imreq = { 0 };
|
||||
struct in_addr iaddr = { 0 };
|
||||
int err = 0;
|
||||
// Configure source interface
|
||||
#if USE_DEFAULT_IF
|
||||
imreq.imr_interface.s_addr = IPADDR_ANY;
|
||||
#else
|
||||
tcpip_adapter_ip_info_t ip_info = { 0 };
|
||||
err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(V4TAG, "Failed to get IP address info. Error 0x%x", err);
|
||||
goto err;
|
||||
}
|
||||
inet_addr_from_ipaddr(&iaddr, &ip_info.ip);
|
||||
#endif
|
||||
// Configure multicast address to listen to
|
||||
err = inet_aton(MULTICAST_IPV4_ADDR, &imreq.imr_multiaddr.s_addr);
|
||||
if (err != 1) {
|
||||
ESP_LOGE(V4TAG, "Configured IPV4 multicast address '%s' is invalid.", MULTICAST_IPV4_ADDR);
|
||||
goto err;
|
||||
}
|
||||
ESP_LOGI(TAG, "Configured IPV4 Multicast address %s", inet_ntoa(imreq.imr_multiaddr.s_addr));
|
||||
if (!IP_MULTICAST(ntohl(imreq.imr_multiaddr.s_addr))) {
|
||||
ESP_LOGW(V4TAG, "Configured IPV4 multicast address '%s' is not a valid multicast address. This will probably not work.", MULTICAST_IPV4_ADDR);
|
||||
}
|
||||
|
||||
if (assign_source_if) {
|
||||
// Assign the IPv4 multicast source interface, via its IP
|
||||
// (only necessary if this socket is IPV4 only)
|
||||
err = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &iaddr,
|
||||
sizeof(struct in_addr));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(V4TAG, "Failed to set IP_MULTICAST_IF. Error %d", errno);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
||||
&imreq, sizeof(struct ip_mreq));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(V4TAG, "Failed to set IP_ADD_MEMBERSHIP. Error %d", errno);
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_EXAMPLE_IPV4 */
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV4_ONLY
|
||||
static int create_multicast_ipv4_socket()
|
||||
{
|
||||
struct sockaddr_in saddr = { 0 };
|
||||
int sock = -1;
|
||||
int err = 0;
|
||||
|
||||
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (sock < 0) {
|
||||
ESP_LOGE(V4TAG, "Failed to create socket. Error %d", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Bind the socket to any address
|
||||
saddr.sin_family = PF_INET;
|
||||
saddr.sin_port = htons(UDP_PORT);
|
||||
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(V4TAG, "Failed to bind socket. Error %d", errno);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
// Assign multicast TTL (set separately from normal interface TTL)
|
||||
uint8_t ttl = MULTICAST_TTL;
|
||||
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(uint8_t));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(V4TAG, "Failed to set IP_MULTICAST_TTL. Error %d", errno);
|
||||
goto err;
|
||||
}
|
||||
|
||||
#if MULTICAST_LOOPBACK
|
||||
// select whether multicast traffic should be received by this device, too
|
||||
// (if setsockopt() is not called, the default is no)
|
||||
uint8_t loopback_val = MULTICAST_LOOPBACK;
|
||||
err = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
&loopback_val, sizeof(uint8_t));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(V4TAG, "Failed to set IP_MULTICAST_LOOP. Error %d", errno);
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
// this is also a listening socket, so add it to the multicast
|
||||
// group for listening...
|
||||
err = socket_add_ipv4_multicast_group(sock, true);
|
||||
if (err < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
// All set, socket is configured for sending and receiving
|
||||
return sock;
|
||||
|
||||
err:
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
#endif /* CONFIG_EXAMPLE_IPV4_ONLY */
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV6
|
||||
static int create_multicast_ipv6_socket()
|
||||
{
|
||||
struct sockaddr_in6 saddr = { 0 };
|
||||
struct in6_addr if_inaddr = { 0 };
|
||||
struct ip6_addr if_ipaddr = { 0 };
|
||||
struct ip6_mreq v6imreq = { 0 };
|
||||
int sock = -1;
|
||||
int err = 0;
|
||||
|
||||
sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IPV6);
|
||||
if (sock < 0) {
|
||||
ESP_LOGE(V6TAG, "Failed to create socket. Error %d", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Bind the socket to any address
|
||||
saddr.sin6_family = AF_INET6;
|
||||
saddr.sin6_port = htons(UDP_PORT);
|
||||
bzero(&saddr.sin6_addr.un, sizeof(saddr.sin6_addr.un));
|
||||
err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(V6TAG, "Failed to bind socket. Error %d", errno);
|
||||
goto err;
|
||||
}
|
||||
|
||||
// Selct the interface to use as multicast source for this socket.
|
||||
#if USE_DEFAULT_IF
|
||||
bzero(&if_inaddr.un, sizeof(if_inaddr.un));
|
||||
#else
|
||||
// Read interface adapter link-local address and use it
|
||||
// to bind the multicast IF to this socket.
|
||||
//
|
||||
// (Note the interface may have other non-LL IPV6 addresses as well,
|
||||
// but it doesn't matter in this context as the address is only
|
||||
// used to identify the interface.)
|
||||
err = tcpip_adapter_get_ip6_linklocal(TCPIP_ADAPTER_IF_STA, &if_ipaddr);
|
||||
inet6_addr_from_ip6addr(&if_inaddr, &if_ipaddr);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(V6TAG, "Failed to get IPV6 LL address. Error 0x%x", err);
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Assign the multicast source interface, via its IP
|
||||
err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_inaddr,
|
||||
sizeof(struct in6_addr));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(V6TAG, "Failed to set IPV6_MULTICAST_IF. Error %d", errno);
|
||||
goto err;
|
||||
}
|
||||
|
||||
// Assign multicast TTL (set separately from normal interface TTL)
|
||||
uint8_t ttl = MULTICAST_TTL;
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(uint8_t));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(V6TAG, "Failed to set IPV6_MULTICAST_HOPS. Error %d", errno);
|
||||
goto err;
|
||||
}
|
||||
|
||||
#if MULTICAST_LOOPBACK
|
||||
// select whether multicast traffic should be received by this device, too
|
||||
// (if setsockopt() is not called, the default is no)
|
||||
uint8_t loopback_val = MULTICAST_LOOPBACK;
|
||||
err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
|
||||
&loopback_val, sizeof(uint8_t));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(V6TAG, "Failed to set IPV6_MULTICAST_LOOP. Error %d", errno);
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
// this is also a listening socket, so add it to the multicast
|
||||
// group for listening...
|
||||
|
||||
// Configure source interface
|
||||
#if USE_DEFAULT_IF
|
||||
v6imreq.imr_interface.s_addr = IPADDR_ANY;
|
||||
#else
|
||||
inet6_addr_from_ip6addr(&v6imreq.ipv6mr_interface, &if_ipaddr);
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_IPV6
|
||||
// Configure multicast address to listen to
|
||||
err = inet6_aton(MULTICAST_IPV6_ADDR, &v6imreq.ipv6mr_multiaddr);
|
||||
if (err != 1) {
|
||||
ESP_LOGE(V6TAG, "Configured IPV6 multicast address '%s' is invalid.", MULTICAST_IPV6_ADDR);
|
||||
goto err;
|
||||
}
|
||||
ESP_LOGI(TAG, "Configured IPV6 Multicast address %s", inet6_ntoa(v6imreq.ipv6mr_multiaddr));
|
||||
ip6_addr_t multi_addr;
|
||||
inet6_addr_to_ip6addr(&multi_addr, &v6imreq.ipv6mr_multiaddr);
|
||||
if (!ip6_addr_ismulticast(&multi_addr)) {
|
||||
ESP_LOGW(V6TAG, "Configured IPV6 multicast address '%s' is not a valid multicast address. This will probably not work.", MULTICAST_IPV6_ADDR);
|
||||
}
|
||||
|
||||
err = setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
|
||||
&v6imreq, sizeof(struct ip6_mreq));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(V6TAG, "Failed to set IPV6_ADD_MEMBERSHIP. Error %d", errno);
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_IPV4_V6
|
||||
// Add the common IPV4 config options
|
||||
err = socket_add_ipv4_multicast_group(sock, false);
|
||||
if (err < 0) {
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_IPV4_V6
|
||||
int only = 0;
|
||||
#else
|
||||
int only = 1; /* IPV6-only socket */
|
||||
#endif
|
||||
err = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &only, sizeof(int));
|
||||
if (err < 0) {
|
||||
ESP_LOGE(V6TAG, "Failed to set IPV6_V6ONLY. Error %d", errno);
|
||||
goto err;
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket set IPV6-only");
|
||||
|
||||
// All set, socket is configured for sending and receiving
|
||||
return sock;
|
||||
|
||||
err:
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void mcast_example_task(void *pvParameters)
|
||||
{
|
||||
while (1) {
|
||||
/* Wait for all the IPs we care about to be set
|
||||
*/
|
||||
uint32_t bits = 0;
|
||||
#ifdef CONFIG_EXAMPLE_IPV4
|
||||
bits |= IPV4_GOTIP_BIT;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_IPV6
|
||||
bits |= IPV6_GOTIP_BIT;
|
||||
#endif
|
||||
ESP_LOGI(TAG, "Waiting for AP connection...");
|
||||
xEventGroupWaitBits(wifi_event_group, bits, false, true, portMAX_DELAY);
|
||||
ESP_LOGI(TAG, "Connected to AP");
|
||||
|
||||
int sock;
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV4_ONLY
|
||||
sock = create_multicast_ipv4_socket();
|
||||
if (sock < 0) {
|
||||
ESP_LOGE(TAG, "Failed to create IPv4 multicast socket");
|
||||
}
|
||||
#else
|
||||
sock = create_multicast_ipv6_socket();
|
||||
if (sock < 0) {
|
||||
ESP_LOGE(TAG, "Failed to create IPv6 multicast socket");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sock < 0) {
|
||||
// Nothing to do!
|
||||
vTaskDelay(5 / portTICK_PERIOD_MS);
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV4
|
||||
// set destination multicast addresses for sending from these sockets
|
||||
struct sockaddr_in sdestv4 = {
|
||||
.sin_family = PF_INET,
|
||||
.sin_port = htons(UDP_PORT),
|
||||
};
|
||||
// We know this inet_aton will pass because we did it above already
|
||||
inet_aton(MULTICAST_IPV4_ADDR, &sdestv4.sin_addr.s_addr);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV6
|
||||
struct sockaddr_in6 sdestv6 = {
|
||||
.sin6_family = PF_INET6,
|
||||
.sin6_port = htons(UDP_PORT),
|
||||
};
|
||||
// We know this inet_aton will pass because we did it above already
|
||||
inet6_aton(MULTICAST_IPV6_ADDR, &sdestv6.sin6_addr);
|
||||
#endif
|
||||
|
||||
// Loop waiting for UDP received, and sending UDP packets if we don't
|
||||
// see any.
|
||||
int err = 1;
|
||||
while (err > 0) {
|
||||
struct timeval tv = {
|
||||
.tv_sec = 2,
|
||||
.tv_usec = 0,
|
||||
};
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(sock, &rfds);
|
||||
|
||||
int s = lwip_select(sock + 1, &rfds, NULL, NULL, &tv);
|
||||
if (s < 0) {
|
||||
ESP_LOGE(TAG, "Select failed: errno %d", errno);
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
else if (s > 0) {
|
||||
if (FD_ISSET(sock, &rfds)) {
|
||||
// Incoming datagram received
|
||||
char recvbuf[48];
|
||||
char raddr_name[32] = { 0 };
|
||||
|
||||
struct sockaddr_in6 raddr; // Large enough for both IPv4 or IPv6
|
||||
socklen_t socklen = sizeof(raddr);
|
||||
int len = recvfrom(sock, recvbuf, sizeof(recvbuf)-1, 0,
|
||||
(struct sockaddr *)&raddr, &socklen);
|
||||
if (len < 0) {
|
||||
ESP_LOGE(TAG, "multicast recvfrom failed: errno %d", errno);
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the sender's address as a string
|
||||
#ifdef CONFIG_EXAMPLE_IPV4
|
||||
if (raddr.sin6_family == PF_INET) {
|
||||
inet_ntoa_r(((struct sockaddr_in *)&raddr)->sin_addr.s_addr,
|
||||
raddr_name, sizeof(raddr_name)-1);
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_IPV6
|
||||
if (raddr.sin6_family == PF_INET6) {
|
||||
inet6_ntoa_r(raddr.sin6_addr, raddr_name, sizeof(raddr_name)-1);
|
||||
}
|
||||
#endif
|
||||
ESP_LOGI(TAG, "received %d bytes from %s:", len, raddr_name);
|
||||
|
||||
recvbuf[len] = 0; // Null-terminate whatever we received and treat like a string...
|
||||
ESP_LOGI(TAG, "%s", recvbuf);
|
||||
}
|
||||
}
|
||||
else { // s == 0
|
||||
// Timeout passed with no incoming data, so send something!
|
||||
static int send_count;
|
||||
const char sendfmt[] = "Multicast #%d sent by ESP32\n";
|
||||
char sendbuf[48];
|
||||
char addrbuf[32] = { 0 };
|
||||
int len = snprintf(sendbuf, sizeof(sendbuf), sendfmt, send_count++);
|
||||
if (len > sizeof(sendbuf)) {
|
||||
ESP_LOGE(TAG, "Overflowed multicast sendfmt buffer!!");
|
||||
send_count = 0;
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
struct addrinfo hints = {
|
||||
.ai_flags = AI_PASSIVE,
|
||||
.ai_socktype = SOCK_DGRAM,
|
||||
};
|
||||
struct addrinfo *res;
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV4 // Send an IPv4 multicast packet
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV4_ONLY
|
||||
hints.ai_family = AF_INET; // For an IPv4 socket
|
||||
#else
|
||||
hints.ai_family = AF_INET6; // For an IPv4 socket with V4 mapped addresses
|
||||
hints.ai_flags |= AI_V4MAPPED;
|
||||
#endif
|
||||
int err = getaddrinfo(CONFIG_EXAMPLE_MULTICAST_IPV4_ADDR,
|
||||
NULL,
|
||||
&hints,
|
||||
&res);
|
||||
if (err < 0) {
|
||||
ESP_LOGE(TAG, "getaddrinfo() failed for IPV4 destination address. error: %d", err);
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_EXAMPLE_IPV4_ONLY
|
||||
((struct sockaddr_in *)res->ai_addr)->sin_port = htons(UDP_PORT);
|
||||
inet_ntoa_r(((struct sockaddr_in *)res->ai_addr)->sin_addr, addrbuf, sizeof(addrbuf)-1);
|
||||
ESP_LOGI(TAG, "Sending to IPV4 multicast address %s...", addrbuf);
|
||||
#else
|
||||
((struct sockaddr_in6 *)res->ai_addr)->sin6_port = htons(UDP_PORT);
|
||||
inet6_ntoa_r(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, addrbuf, sizeof(addrbuf)-1);
|
||||
ESP_LOGI(TAG, "Sending to IPV6 (V4 mapped) multicast address %s (%s)...", addrbuf, CONFIG_EXAMPLE_MULTICAST_IPV4_ADDR);
|
||||
#endif
|
||||
err = sendto(sock, sendbuf, len, 0, res->ai_addr, res->ai_addrlen);
|
||||
if (err < 0) {
|
||||
ESP_LOGE(TAG, "IPV4 sendto failed. errno: %d", errno);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_IPV6
|
||||
hints.ai_family = AF_INET6;
|
||||
hints.ai_protocol = 0;
|
||||
err = getaddrinfo(CONFIG_EXAMPLE_MULTICAST_IPV6_ADDR,
|
||||
NULL,
|
||||
&hints,
|
||||
&res);
|
||||
if (err < 0) {
|
||||
ESP_LOGE(TAG, "getaddrinfo() failed for IPV6 destination address. error: %d", err);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
struct sockaddr_in6 *s6addr = (struct sockaddr_in6 *)res->ai_addr;
|
||||
s6addr->sin6_port = htons(UDP_PORT);
|
||||
inet6_ntoa_r(s6addr->sin6_addr, addrbuf, sizeof(addrbuf)-1);
|
||||
ESP_LOGI(TAG, "Sending to IPV6 multicast address %s...", addrbuf);
|
||||
err = sendto(sock, sendbuf, len, 0, res->ai_addr, res->ai_addrlen);
|
||||
if (err < 0) {
|
||||
ESP_LOGE(TAG, "IPV6 sendto failed. errno: %d", errno);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "Shutting down socket and restarting...");
|
||||
shutdown(sock, 0);
|
||||
close(sock);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
ESP_ERROR_CHECK( nvs_flash_init() );
|
||||
initialise_wifi();
|
||||
xTaskCreate(&mcast_example_task, "mcast_task", 4096, NULL, 5, NULL);
|
||||
}
|
Loading…
Reference in a new issue