[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