[ovs-dev] [PATCH 3/3] Multicast Listener Discovery support
Flavio Leitner
fbl at sysclose.org
Thu Jun 25 20:25:13 UTC 2015
On Tue, Jun 23, 2015 at 05:03:16PM -0300, Thadeu Lima de Souza Cascardo wrote:
> Add support for MLDv1 and MLDv2. The behavior is not that different from
> IGMP. Packets to all-hosts address and queries are always flooded,
> reports go to routers, routers are added when a query is observed, and
> all MLD packets go through slow path.
>
> Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo at redhat.com>
> ---
> lib/flow.h | 25 +++++++++++++
> lib/mcast-snooping.c | 67 ++++++++++++++++++++++++++++++++++
> lib/mcast-snooping.h | 4 +++
> lib/packets.c | 1 +
> lib/packets.h | 40 +++++++++++++++++++++
> ofproto/ofproto-dpif-xlate.c | 85 +++++++++++++++++++++++++++++++++++++-------
> 6 files changed, 209 insertions(+), 13 deletions(-)
>
> diff --git a/lib/flow.h b/lib/flow.h
> index 70554e4..e68dd74 100644
> --- a/lib/flow.h
> +++ b/lib/flow.h
> @@ -758,6 +758,31 @@ static inline bool is_icmpv6(const struct flow *flow)
> && flow->nw_proto == IPPROTO_ICMPV6);
> }
>
> +static inline bool is_igmp(const struct flow *flow)
> +{
> + return (flow->dl_type == htons(ETH_TYPE_IP)
> + && flow->nw_proto == IPPROTO_IGMP);
> +}
> +
> +static inline bool is_mld(const struct flow *flow)
> +{
> + return is_icmpv6(flow)
> + && (flow->tp_src == htons(MLD_QUERY)
> + || flow->tp_src == htons(MLD_REPORT)
> + || flow->tp_src == htons(MLD_DONE)
> + || flow->tp_src == htons(MLD2_REPORT));
> +}
> +
> +static inline bool is_mld_query(const struct flow *flow)
> +{
> + return is_icmpv6(flow) && flow->tp_src == htons(MLD_QUERY);
> +}
> +
> +static inline bool is_mld_report(const struct flow *flow)
> +{
> + return is_mld(flow) && !is_mld_query(flow);
> +}
> +
> static inline bool is_stp(const struct flow *flow)
> {
> return (eth_addr_equals(flow->dl_dst, eth_addr_stp)
> diff --git a/lib/mcast-snooping.c b/lib/mcast-snooping.c
> index f2684f3..aee80b1 100644
> --- a/lib/mcast-snooping.c
> +++ b/lib/mcast-snooping.c
> @@ -499,6 +499,73 @@ mcast_snooping_add_report(struct mcast_snooping *ms,
> return count;
> }
>
> +int
> +mcast_snooping_add_mld(struct mcast_snooping *ms,
> + const struct dp_packet *p,
> + uint16_t vlan, void *port)
> +{
> + const struct in6_addr *addr;
> + size_t offset;
> + const struct mld_header *mld;
> + const struct mld2_record *record;
> + int count = 0;
> + int ngrp;
> + bool ret;
> +
> + offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
> + mld = dp_packet_at(p, offset, MLD_HEADER_LEN);
> + if (!mld) {
> + return 0;
> + }
> + ngrp = ntohs(mld->ngrp);
> + offset += MLD_HEADER_LEN;
> + addr = dp_packet_at(p, offset, sizeof(struct in6_addr));
> +
> + switch (mld->type) {
> + case MLD_REPORT:
> + ret = mcast_snooping_add_group(ms, addr, vlan, port);
> + if (ret)
> + count++;
> + break;
> + case MLD_DONE:
> + ret = mcast_snooping_leave_group(ms, addr, vlan, port);
> + if (ret)
> + count++;
> + break;
> + case MLD2_REPORT:
> + while (ngrp--) {
> + record = dp_packet_at(p, offset, sizeof(struct mld2_record));
> + if (!record) {
> + break;
> + }
> + /* Only consider known record types. */
> + if (record->type < IGMPV3_MODE_IS_INCLUDE
> + || record->type > IGMPV3_BLOCK_OLD_SOURCES) {
> + continue;
> + }
> + addr = &record->maddr;
> + /*
> + * If record is INCLUDE MODE and there are no sources, it's equivalent
> + * to a LEAVE.
> + */
> + if (ntohs(record->nsrcs) == 0
> + && (record->type == IGMPV3_MODE_IS_INCLUDE
> + || record->type == IGMPV3_CHANGE_TO_INCLUDE_MODE)) {
> + ret = mcast_snooping_leave_group(ms, addr, vlan, port);
> + } else {
> + ret = mcast_snooping_add_group(ms, addr, vlan, port);
> + }
> + if (ret) {
> + count++;
> + }
> + offset += sizeof(*record)
> + + ntohs(record->nsrcs) * sizeof(struct in6_addr) + record->aux_len;
> + }
> + }
> +
> + return count;
> +}
> +
> bool
> mcast_snooping_leave_group(struct mcast_snooping *ms,
> const struct in6_addr *addr,
> diff --git a/lib/mcast-snooping.h b/lib/mcast-snooping.h
> index e3d15e4..99c314d 100644
> --- a/lib/mcast-snooping.h
> +++ b/lib/mcast-snooping.h
> @@ -194,6 +194,10 @@ int mcast_snooping_add_report(struct mcast_snooping *ms,
> const struct dp_packet *p,
> uint16_t vlan, void *port)
> OVS_REQ_WRLOCK(ms->rwlock);
> +int mcast_snooping_add_mld(struct mcast_snooping *ms,
> + const struct dp_packet *p,
> + uint16_t vlan, void *port)
> + OVS_REQ_WRLOCK(ms->rwlock);
> bool mcast_snooping_leave_group(struct mcast_snooping *ms,
> const struct in6_addr *addr,
> uint16_t vlan, void *port)
> diff --git a/lib/packets.c b/lib/packets.c
> index d04fffc..c7ea24c 100644
> --- a/lib/packets.c
> +++ b/lib/packets.c
> @@ -34,6 +34,7 @@
> #include "unaligned.h"
>
> const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT;
> +const struct in6_addr in6addr_all_hosts = IN6ADDR_ALL_HOSTS_INIT;
>
> /* Parses 's' as a 16-digit hexadecimal number representing a datapath ID. On
> * success stores the dpid into '*dpidp' and returns true, on failure stores 0
> diff --git a/lib/packets.h b/lib/packets.h
> index b5cd6ab..136376b 100644
> --- a/lib/packets.h
> +++ b/lib/packets.h
> @@ -568,6 +568,9 @@ BUILD_ASSERT_DECL(IGMPV3_RECORD_LEN == sizeof(struct igmpv3_record));
> #define IGMP_HOST_LEAVE_MESSAGE 0x17
> #define IGMPV3_HOST_MEMBERSHIP_REPORT 0x22 /* V3 version of 0x12 */
>
> +/*
> + * IGMPv3 and MLDv2 use the same codes.
> + */
> #define IGMPV3_MODE_IS_INCLUDE 1
> #define IGMPV3_MODE_IS_EXCLUDE 2
> #define IGMPV3_CHANGE_TO_INCLUDE_MODE 3
> @@ -575,6 +578,35 @@ BUILD_ASSERT_DECL(IGMPV3_RECORD_LEN == sizeof(struct igmpv3_record));
> #define IGMPV3_ALLOW_NEW_SOURCES 5
> #define IGMPV3_BLOCK_OLD_SOURCES 6
>
> +/*
> + * Use the same struct for MLD and MLD2, naming members as the defined fields in
> + * in the corresponding version of the protocol, though they are reserved in the
> + * other one.
> + */
> +#define MLD_HEADER_LEN 8
> +struct mld_header {
> + uint8_t type;
> + uint8_t code;
> + ovs_be16 csum;
> + ovs_be16 mrd;
> + ovs_be16 ngrp;
> +};
> +BUILD_ASSERT_DECL(MLD_HEADER_LEN == sizeof(struct mld_header));
> +
> +#define MLD2_RECORD_LEN 20
> +struct mld2_record {
> + uint8_t type;
> + uint8_t aux_len;
> + ovs_be16 nsrcs;
> + struct in6_addr maddr;
> +};
> +BUILD_ASSERT_DECL(MLD2_RECORD_LEN == sizeof(struct mld2_record));
> +
> +#define MLD_QUERY 130
> +#define MLD_REPORT 131
> +#define MLD_DONE 132
> +#define MLD2_REPORT 143
> +
> #define SCTP_HEADER_LEN 12
> struct sctp_header {
> ovs_be16 sctp_src;
> @@ -726,6 +758,10 @@ extern const struct in6_addr in6addr_exact;
> #define IN6ADDR_EXACT_INIT { { { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, \
> 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff } } }
>
> +extern const struct in6_addr in6addr_all_hosts;
> +#define IN6ADDR_ALL_HOSTS_INIT { { { 0xff,0x02,0x00,0x00,0x00,0x00,0x00,0x00, \
> + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 } } }
> +
> static inline bool ipv6_addr_equals(const struct in6_addr *a,
> const struct in6_addr *b)
> {
> @@ -744,6 +780,10 @@ static inline bool ipv6_mask_is_exact(const struct in6_addr *mask) {
> return ipv6_addr_equals(mask, &in6addr_exact);
> }
>
> +static inline bool ipv6_is_all_hosts(const struct in6_addr *addr) {
> + return ipv6_addr_equals(addr, &in6addr_all_hosts);
> +}
> +
> static inline bool dl_type_is_ip_any(ovs_be16 dl_type)
> {
> return dl_type == htons(ETH_TYPE_IP)
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 1490f0f..e48f872 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -1998,12 +1998,12 @@ update_learning_table(const struct xbridge *xbridge,
> /* Updates multicast snooping table 'ms' given that a packet matching 'flow'
> * was received on 'in_xbundle' in 'vlan' and is either Report or Query. */
> static void
> -update_mcast_snooping_table__(const struct xbridge *xbridge,
> - const struct flow *flow,
> - struct mcast_snooping *ms,
> - ovs_be32 ip4, int vlan,
> - struct xbundle *in_xbundle,
> - const struct dp_packet *packet)
> +update_mcast_snooping_table4__(const struct xbridge *xbridge,
> + const struct flow *flow,
> + struct mcast_snooping *ms,
> + ovs_be32 ip4, int vlan,
> + struct xbundle *in_xbundle,
> + const struct dp_packet *packet)
> OVS_REQ_WRLOCK(ms->rwlock)
> {
> static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 30);
> @@ -2045,6 +2045,39 @@ update_mcast_snooping_table__(const struct xbridge *xbridge,
> }
> }
>
> +static void
> +update_mcast_snooping_table6__(const struct xbridge *xbridge,
> + const struct flow *flow,
> + struct mcast_snooping *ms, int vlan,
> + struct xbundle *in_xbundle,
> + const struct dp_packet *packet)
> + OVS_REQ_WRLOCK(ms->rwlock)
> +{
> + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 30);
> + int count;
> +
> + switch (ntohs(flow->tp_src)) {
> + case MLD_QUERY:
> + if (ipv6_addr_equals(&flow->ipv6_src, &in6addr_any)
> + && mcast_snooping_add_mrouter(ms, vlan, in_xbundle->ofbundle)) {
> + VLOG_DBG_RL(&rl, "bridge %s: multicast snooping query on port %s"
> + "in VLAN %d",
> + xbridge->name, in_xbundle->name, vlan);
> + }
> + break;
> + case MLD_REPORT:
> + case MLD_DONE:
> + case MLD2_REPORT:
> + if ((count = mcast_snooping_add_mld(ms, packet, vlan,
> + in_xbundle->ofbundle))) {
> + VLOG_DBG_RL(&rl, "bridge %s: multicast snooping processed %d "
> + "addresses on port %s in VLAN %d",
> + xbridge->name, count, in_xbundle->name, vlan);
> + }
> + break;
> + }
> +}
> +
> /* Updates multicast snooping table 'ms' given that a packet matching 'flow'
> * was received on 'in_xbundle' in 'vlan'. */
> static void
> @@ -2075,8 +2108,13 @@ update_mcast_snooping_table(const struct xbridge *xbridge,
> }
>
> if (!mcast_xbundle || mcast_xbundle != in_xbundle) {
> - update_mcast_snooping_table__(xbridge, flow, ms, flow->igmp_group_ip4,
> - vlan, in_xbundle, packet);
> + if (flow->dl_type == htons(ETH_TYPE_IP))
> + update_mcast_snooping_table4__(xbridge, flow, ms,
> + flow->igmp_group_ip4, vlan,
> + in_xbundle, packet);
Since we have one call for each protocol, it seems that flow->igmp_group_ip4
is unneeded at this point and we can have IPv6 and IPv4 calls symmetric.
Thanks,
fbl
> + else
> + update_mcast_snooping_table6__(xbridge, flow, ms, vlan,
> + in_xbundle, packet);
> }
> ovs_rwlock_unlock(&ms->rwlock);
> }
> @@ -2280,11 +2318,11 @@ xlate_normal(struct xlate_ctx *ctx)
> if (mcast_snooping_enabled(ctx->xbridge->ms)
> && !eth_addr_is_broadcast(flow->dl_dst)
> && eth_addr_is_multicast(flow->dl_dst)
> - && flow->dl_type == htons(ETH_TYPE_IP)) {
> + && is_ip_any(flow)) {
> struct mcast_snooping *ms = ctx->xbridge->ms;
> - struct mcast_group *grp;
> + struct mcast_group *grp = NULL;
>
> - if (flow->nw_proto == IPPROTO_IGMP) {
> + if (is_igmp(flow)) {
> if (mcast_snooping_is_membership(flow->tp_src) ||
> mcast_snooping_is_query(flow->tp_src)) {
> if (ctx->xin->may_learn) {
> @@ -2317,8 +2355,26 @@ xlate_normal(struct xlate_ctx *ctx)
> xlate_normal_flood(ctx, in_xbundle, vlan);
> }
> return;
> + } else if (is_mld(flow)) {
> + ctx->xout->slow |= SLOW_ACTION;
> + if (ctx->xin->may_learn) {
> + update_mcast_snooping_table(ctx->xbridge, flow, vlan,
> + in_xbundle, ctx->xin->packet);
> + }
> + if (is_mld_report(flow)) {
> + ovs_rwlock_rdlock(&ms->rwlock);
> + xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan);
> + xlate_normal_mcast_send_rports(ctx, ms, in_xbundle, vlan);
> + ovs_rwlock_unlock(&ms->rwlock);
> + } else {
> + xlate_report(ctx, "MLD query, flooding");
> + xlate_normal_flood(ctx, in_xbundle, vlan);
> + }
> } else {
> - if (ip_is_local_multicast(flow->nw_dst)) {
> + if ((flow->dl_type == htons(ETH_TYPE_IP)
> + && ip_is_local_multicast(flow->nw_dst))
> + || (flow->dl_type == htons(ETH_TYPE_IPV6)
> + && ipv6_is_all_hosts(&flow->ipv6_dst))) {
> /* RFC4541: section 2.1.2, item 2: Packets with a dst IP
> * address in the 224.0.0.x range which are not IGMP must
> * be forwarded on all ports */
> @@ -2330,7 +2386,10 @@ xlate_normal(struct xlate_ctx *ctx)
>
> /* forwarding to group base ports */
> ovs_rwlock_rdlock(&ms->rwlock);
> - grp = mcast_snooping_lookup4(ms, flow->nw_dst, vlan);
> + if (flow->dl_type == htons(ETH_TYPE_IP))
> + grp = mcast_snooping_lookup4(ms, flow->nw_dst, vlan);
> + else if (flow->dl_type == htons(ETH_TYPE_IPV6))
> + grp = mcast_snooping_lookup(ms, &flow->ipv6_dst, vlan);
> if (grp) {
> xlate_normal_mcast_send_group(ctx, ms, grp, in_xbundle, vlan);
> xlate_normal_mcast_send_fports(ctx, ms, in_xbundle, vlan);
> --
> 2.4.2
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
More information about the dev
mailing list