udp: add core-level multicast support for IPv6

So far, the UDP core module implemented only IPv4 multicast support.
This patch extends the module with the features necessary for socket
layers on top to implement IPv6 multicast support as well:

o If a UDP PCB is bound to an IPv6 multicast address, a unicast source
  address is selected and used to send the packet instead, as is
  required (and was the case for IPv4 multicast already).

o Unlike IPv4's IP_MULTICAST_IF socket option, which takes a source
  IPv4 address, the IPV6_MULTICAST_IF socket option (from RFC 3493)
  takes an interface identifier to denote the interface to use for
  outgoing multicast-destined packets. A new pair of UDP PCB API
  calls, udp_[gs]et_multicast_netif_index(), are added to support
  this. The new definition "NETIF_NO_INDEX" may be used to indicate
  that lwIP should pick an interface instead.

  IPv4 socket implementations may now also choose to map the given
  source address to an interface index immediately and use the new
  facility instead of the old udp_[gs]et_multicast_netif_addr() one.
  A side effect of limiting the old facility to IPv4 is that for dual-
  stack configurations with multicast support, the UDP PCB size is
  reduced by (up to) 16 bytes.

o For configurations that enable loopback interface support, the IPv6
  code now also supports multicast loopback (IPV6_MULTICAST_LOOP).

o The LWIP_MULTICAST_TX_OPTIONS opt.h setting now covers both IPv4
  and IPv6, and as such is no longer strictly linked to IGMP. It is
  therefore placed in its own lwIP options subgroup in opt.h.

The IPV6_MULTICAST_HOPS socket option can already be implemented using
the existing IP_MULTICAST_TTL support, and thus requires no additional
changes. Overall, this patch should not break any existing code.
This commit is contained in:
David van Moolenbroek 2017-02-06 14:12:37 +00:00 committed by Dirk Ziegelmeier
parent 4a9db56f4c
commit ab8119360e
7 changed files with 90 additions and 41 deletions

View File

@ -2020,7 +2020,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
s, *(int *)optval));
break;
#if LWIP_MULTICAST_TX_OPTIONS
#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS
case IP_MULTICAST_TTL:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
@ -2049,7 +2049,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
s, *(int *)optval));
break;
#endif /* LWIP_MULTICAST_TX_OPTIONS */
#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
s, optname));
@ -2389,7 +2389,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
s, sock->conn->pcb.ip->tos));
break;
#if LWIP_MULTICAST_TX_OPTIONS
#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS
case IP_MULTICAST_TTL:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t*)optval));
@ -2410,7 +2410,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
}
break;
#endif /* LWIP_MULTICAST_TX_OPTIONS */
#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */
#if LWIP_IGMP
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:

View File

@ -96,7 +96,7 @@ PACK_STRUCT_END
#error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
#endif
#if (!LWIP_UDP && LWIP_MULTICAST_TX_OPTIONS)
#error "If you want to use IGMP/LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 in your lwipopts.h"
#error "If you want to use LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 in your lwipopts.h"
#endif
#if (!LWIP_UDP && LWIP_DNS)
#error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
@ -123,9 +123,6 @@ PACK_STRUCT_END
#if (LWIP_IGMP && !LWIP_IPV4)
#error "IGMP needs LWIP_IPV4 enabled in your lwipopts.h"
#endif
#if (LWIP_MULTICAST_TX_OPTIONS && !LWIP_IPV4)
#error "LWIP_MULTICAST_TX_OPTIONS needs LWIP_IPV4 enabled in your lwipopts.h"
#endif
#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
#error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
#endif

View File

@ -1028,6 +1028,11 @@ ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
}
}
}
#if LWIP_MULTICAST_TX_OPTIONS
if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
netif_loop_output(netif, p);
}
#endif /* LWIP_MULTICAST_TX_OPTIONS */
#endif /* ENABLE_LOOPBACK */
#if LWIP_IPV6_FRAG
/* don't fragment if interface has mtu set to 0 [loopif] */

View File

@ -514,7 +514,7 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
{
#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
struct netif *netif;
const ip_addr_t *dst_ip_route = dst_ip;
const ip_addr_t *src_ip_route;
if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
return ERR_VAL;
@ -522,8 +522,31 @@ 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_MULTICAST_TX_OPTIONS
if (ip_addr_ismulticast(dst_ip_route)) {
if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
/* Don't call ip_route() with IP_ANY_TYPE */
src_ip_route = IP46_ADDR_ANY(IP_GET_TYPE(dst_ip));
} else {
src_ip_route = &pcb->local_ip;
}
#if LWIP_MULTICAST_TX_OPTIONS
netif = NULL;
if (ip_addr_ismulticast(dst_ip)) {
/* For IPv6, the interface to use for packets with a multicast destination
* is specified using an interface index. The same approach may be used for
* IPv4 as well, in which case it overrides the IPv4 multicast override
* address below. Here we have to look up the netif by going through the
* list, but by doing so we skip a route lookup. If the interface index has
* gone stale, we fall through and do the regular route lookup after all. */
if (pcb->mcast_ifindex != NETIF_NO_INDEX) {
for (netif = netif_list; netif != NULL; netif = netif->next) {
if (pcb->mcast_ifindex == netif_num_to_index(netif)) {
break; /* found! */
}
}
}
#if LWIP_IPV4
else
#if LWIP_IPV6
if (IP_IS_V4(dst_ip))
#endif /* LWIP_IPV6 */
@ -531,21 +554,21 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
/* IPv4 does not use source-based routing by default, so we use an
administratively selected interface for multicast by default.
However, this can be overridden by setting an interface address
in pcb->multicast_ip that is used for routing. */
if (!ip_addr_isany_val(pcb->multicast_ip) &&
!ip4_addr_cmp(ip_2_ip4(&pcb->multicast_ip), IP4_ADDR_BROADCAST)) {
dst_ip_route = &pcb->multicast_ip;
in pcb->mcast_ip4 that is used for routing. If this routing lookup
fails, we try regular routing as though no override was set. */
if (!ip4_addr_isany_val(pcb->mcast_ip4) &&
!ip4_addr_cmp(&pcb->mcast_ip4, IP4_ADDR_BROADCAST)) {
netif = ip4_route_src(ip_2_ip4(src_ip_route), &pcb->mcast_ip4);
}
}
#endif /* LWIP_IPV4 */
}
#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */
/* find the outgoing network interface for this packet */
if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
/* Don't call ip_route() with IP_ANY_TYPE */
netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(dst_ip_route)), dst_ip_route);
} else {
netif = ip_route(&pcb->local_ip, dst_ip_route);
if (netif == NULL)
#endif /* LWIP_MULTICAST_TX_OPTIONS */
{
/* find the outgoing network interface for this packet */
netif = ip_route(src_ip_route, dst_ip);
}
/* no outgoing network interface could be found? */
@ -604,10 +627,11 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_i
return ERR_VAL;
}
/* PCB local address is IP_ANY_ADDR? */
/* PCB local address is IP_ANY_ADDR or multicast? */
#if LWIP_IPV6
if (IP_IS_V6(dst_ip)) {
if (ip6_addr_isany(ip_2_ip6(&pcb->local_ip))) {
if (ip6_addr_isany(ip_2_ip6(&pcb->local_ip)) ||
ip6_addr_ismulticast(ip_2_ip6(&pcb->local_ip))) {
src_ip = ip6_select_source_address(netif, ip_2_ip6(dst_ip));
if (src_ip == NULL) {
/* No suitable source address was found. */
@ -737,11 +761,11 @@ udp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *d
udphdr->chksum = 0x0000;
/* Multicast Loop? */
#if (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD)
#if LWIP_MULTICAST_TX_OPTIONS
if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) {
q->flags |= PBUF_FLAG_MCASTLOOP;
}
#endif /* (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD) */
#endif /* LWIP_MULTICAST_TX_OPTIONS */
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len));
@ -917,7 +941,7 @@ udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
* This is legacy support: scope-aware callers should always provide properly
* zoned source addresses. Do the zone selection before the address-in-use
* check below; as such we have to make a temporary copy of the address. */
if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNICAST)) {
if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNKNOWN)) {
ip_addr_copy(zoned_ipaddr, *ipaddr);
ip6_addr_select_zone(ip_2_ip6(&zoned_ipaddr), ip_2_ip6(&zoned_ipaddr));
ipaddr = &zoned_ipaddr;

View File

@ -504,6 +504,7 @@ char * netif_index_to_name(u8_t idx, char *name);
/* Interface indexes always start at 1 per RFC 3493, section 4, num starts at 0 */
#define netif_num_to_index(netif) ((netif)->num + 1)
#define netif_index_to_num(index) ((index) - 1)
#define NETIF_NO_INDEX (0)
#ifdef __cplusplus
}

View File

@ -972,7 +972,29 @@
/*
----------------------------------
----- Multicast/IGMP options -----
-------- Multicast options -------
----------------------------------
*/
/**
* @defgroup lwip_opts_multicast Multicast
* @ingroup lwip_opts_infrastructure
* @{
*/
/**
* LWIP_MULTICAST_TX_OPTIONS==1: Enable multicast TX support like the socket options
* IP_MULTICAST_TTL/IP_MULTICAST_IF/IP_MULTICAST_LOOP, as well as (currently only)
* core support for the corresponding IPv6 options.
*/
#if !defined LWIP_MULTICAST_TX_OPTIONS || defined __DOXYGEN__
#define LWIP_MULTICAST_TX_OPTIONS ((LWIP_IGMP || LWIP_IPV6_MLD) && LWIP_UDP)
#endif
/**
* @}
*/
/*
----------------------------------
---------- IGMP options ----------
----------------------------------
*/
/**
@ -990,14 +1012,6 @@
#undef LWIP_IGMP
#define LWIP_IGMP 0
#endif
/**
* LWIP_MULTICAST_TX_OPTIONS==1: Enable multicast TX support like the socket options
* IP_MULTICAST_TTL/IP_MULTICAST_IF/IP_MULTICAST_LOOP
*/
#if !defined LWIP_MULTICAST_TX_OPTIONS || defined __DOXYGEN__
#define LWIP_MULTICAST_TX_OPTIONS (LWIP_IGMP && LWIP_UDP)
#endif
/**
* @}
*/

View File

@ -91,8 +91,12 @@ struct udp_pcb {
u16_t local_port, remote_port;
#if LWIP_MULTICAST_TX_OPTIONS
/** outgoing network interface for multicast packets */
ip_addr_t multicast_ip;
#if LWIP_IPV4
/** outgoing network interface for multicast packets, by IPv4 address (if not 'any') */
ip4_addr_t mcast_ip4;
#endif /* LWIP_IPV4 */
/** outgoing network interface for multicast packets, by interface index (if nonzero) */
u8_t mcast_ifindex;
/** TTL for outgoing multicast packets */
u8_t mcast_ttl;
#endif /* LWIP_MULTICAST_TX_OPTIONS */
@ -159,9 +163,13 @@ void udp_init (void);
#define udp_new_ip6() udp_new_ip_type(IPADDR_TYPE_V6)
#if LWIP_MULTICAST_TX_OPTIONS
#define udp_set_multicast_netif_addr(pcb, ip4addr) ip_addr_copy_from_ip4((pcb)->multicast_ip, *(ip4addr))
#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)
#if LWIP_IPV4
#define udp_set_multicast_netif_addr(pcb, ip4addr) ip4_addr_copy((pcb)->mcast_ip4, *(ip4addr))
#define udp_get_multicast_netif_addr(pcb) (&(pcb)->mcast_ip4)
#endif /* LWIP_IPV4 */
#define udp_set_multicast_netif_index(pcb, idx) ((pcb)->mcast_ifindex = (idx))
#define udp_get_multicast_netif_index(pcb) ((pcb)->mcast_ifindex)
#define udp_set_multicast_ttl(pcb, value) ((pcb)->mcast_ttl = (value))
#define udp_get_multicast_ttl(pcb) ((pcb)->mcast_ttl)
#endif /* LWIP_MULTICAST_TX_OPTIONS */