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 <casca...@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@openvswitch.org > http://openvswitch.org/mailman/listinfo/dev _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev