WARNING: This patch *only* compiles! NOT tested yet! Based on patch 1/2, this patch adds IPv6 support.
Any comments are welcome! Cc: Ben Pfaff <b...@nicira.com> Cc: Jesse Gross <je...@nicira.com> Not-Yet-Signed-off-by: Cong Wang <amw...@redhat.com> --- lib/flow.c | 18 +++++++++--- lib/flow.h | 12 +++++++- lib/igmp-snooping.c | 70 ++++++++++++++++++++++++++++++++++++++--------- lib/igmp-snooping.h | 11 +++++--- lib/packets.h | 11 +++++++ ofproto/ofproto-dpif.c | 64 ++++++++++++++++++++++++++++++++----------- 6 files changed, 144 insertions(+), 42 deletions(-) diff --git a/lib/flow.c b/lib/flow.c index 9cca7d9..cc515aa 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -286,9 +286,11 @@ parse_icmpv6(struct ofpbuf *b, struct flow *flow) flow->tp_src = htons(icmp->icmp6_type); flow->tp_dst = htons(icmp->icmp6_code); - if (icmp->icmp6_code == 0 && - (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT || - icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) { + if (icmp->icmp6_code != 0) + return true; + + if (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT || + icmp->icmp6_type == ND_NEIGHBOR_ADVERT) { const struct in6_addr *nd_target; nd_target = ofpbuf_try_pull(b, sizeof *nd_target); @@ -330,6 +332,12 @@ parse_icmpv6(struct ofpbuf *b, struct flow *flow) goto invalid; } } + } else if (icmp->icmp6_type == ICMPV6_MGM_REPORT || + icmp->icmp6_type == ICMPV6_MGM_REDUCTION) { + const struct mld_hdr *mld = b->data; + if (b->size < sizeof(*mld)) + goto invalid; + flow->igmp_group.u.ip6 = mld->mld_addr; } return true; @@ -471,7 +479,7 @@ flow_extract_l3_onwards(struct ofpbuf *packet, struct flow *flow, if (igmp) { flow->tp_src = htons(igmp->igmp_type); flow->tp_dst = htons(igmp->igmp_code); - flow->igmp_group = htons(igmp->group); + flow->igmp_group.u.ip4 = htons(igmp->group); } } } @@ -944,7 +952,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) b->l4 = igmp = ofpbuf_put_zeros(b, sizeof *igmp); igmp->igmp_type = ntohs(flow->tp_src); igmp->igmp_code = ntohs(flow->tp_dst); - igmp->group = ntohs(flow->igmp_group); + igmp->group = ntohs(flow->igmp_group.u.ip4); igmp->igmp_csum = csum(igmp, IGMP_HEADER_LEN); } } diff --git a/lib/flow.h b/lib/flow.h index 4b540f9..3276a3c 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -59,6 +59,14 @@ BUILD_ASSERT_DECL(FLOW_NW_FRAG_LATER == NX_IP_FRAG_LATER); const char *flow_tun_flag_to_string(uint32_t flags); +struct mdb_ip { + union { + ovs_be32 ip4; + struct in6_addr ip6; + } u; + uint8_t proto; +}; + struct flow_tnl { ovs_be64 tun_id; ovs_be32 ip_src; @@ -98,7 +106,7 @@ struct flow { ovs_be16 encap_dl_type; /* MPLS encapsulated Ethernet frame type */ ovs_be16 tp_src; /* TCP/UDP source port. */ ovs_be16 tp_dst; /* TCP/UDP destination port. */ - ovs_be32 igmp_group; /* IGMP multicast group. */ + struct mdb_ip igmp_group; /* IGMP multicast group. */ uint8_t dl_src[6]; /* Ethernet source address. */ uint8_t dl_dst[6]; /* Ethernet destination address. */ uint8_t nw_proto; /* IP protocol or low 8 bits of ARP opcode. */ @@ -114,7 +122,7 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0); #define FLOW_U32S (sizeof(struct flow) / 4) /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ -BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 168 && +BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 184 && FLOW_WC_SEQ == 19); /* Represents the metadata fields of struct flow. */ diff --git a/lib/igmp-snooping.c b/lib/igmp-snooping.c index 3e96b38..22185ec 100644 --- a/lib/igmp-snooping.c +++ b/lib/igmp-snooping.c @@ -46,10 +46,17 @@ mdb_entry_age(const struct igmp_mdb *mdb, const struct mdb_entry *e) } static uint32_t -igmp_mdb_hash(const struct igmp_mdb *mdb, ovs_be32 grp, +igmp_mdb_hash(const struct igmp_mdb *mdb, struct mdb_ip *grp, uint16_t vlan) { - return hash_3words(grp, vlan, mdb->secret); + if (grp->proto == htons(ETH_TYPE_IP)) { + return hash_3words(grp->u.ip4, vlan, mdb->secret); + } else { + uint32_t tmp[sizeof(*grp)/sizeof(uint32_t) + 1] = {0}; + memcpy(tmp, grp, sizeof(*grp)); + tmp[sizeof(tmp)/sizeof(uint32_t) - 1] = vlan; + return hash_words(tmp, sizeof(tmp)/sizeof(uint32_t), mdb->secret); + } } static struct mdb_entry * @@ -63,22 +70,27 @@ mdb_entry_from_lru_node(struct list *list) * flooded to be revalidated.) */ static tag_type make_unknown_mdb_tag(const struct igmp_mdb *mdb, - ovs_be32 grp, uint16_t vlan) + struct mdb_ip *grp, uint16_t vlan) { - return tag_create_deterministic(igmp_mdb_hash(mdb, grp, vlan)); + return tag_create_deterministic(igmp_mdb_hash(mdb, grp, vlan)); +} + +static bool mdb_entry_match(struct mdb_entry *e, struct mdb_ip *grp, uint16_t vlan) +{ + return e->vlan == vlan && + ((grp->proto == htons(ETH_TYPE_IP) && grp->u.ip4 == e->group.u.ip4) || + (grp->proto == htons(ETH_TYPE_IPV6) && ipv6_addr_equals(&e->group.u.ip6, &grp->u.ip6))); } static struct mdb_entry * -mdb_entry_lookup(const struct igmp_mdb *mdb, - ovs_be32 grp, uint16_t vlan) +mdb_entry_lookup(const struct igmp_mdb *mdb, struct mdb_ip *grp, uint16_t vlan) { struct mdb_entry *e; HMAP_FOR_EACH_WITH_HASH (e, hmap_node, igmp_mdb_hash(mdb, grp, vlan), &mdb->table) { - if (e->vlan == vlan && e->group == grp) { + if (mdb_entry_match(e, grp, vlan)) return e; - } } return NULL; } @@ -211,7 +223,7 @@ igmp_may_snoop(const struct igmp_mdb *mdb, const ovs_be32 dst, uint16_t vlan) * 'port' member. Otherwise calling those functions is at the caller's * discretion. */ struct mdb_entry * -igmp_mdb_insert(struct igmp_mdb *mdb, ovs_be32 grp, uint16_t vlan) +igmp_mdb_insert(struct igmp_mdb *mdb, const struct mdb_ip *grp, uint16_t vlan) { struct mdb_entry *e; @@ -226,7 +238,7 @@ igmp_mdb_insert(struct igmp_mdb *mdb, ovs_be32 grp, uint16_t vlan) e = xmalloc(sizeof *e); hmap_insert(&mdb->table, &e->hmap_node, hash); - e->group = grp; + e->group = *grp; e->vlan = vlan; e->tag = 0; } else { @@ -255,7 +267,7 @@ igmp_mdb_changed(struct igmp_mdb *mdb, struct mdb_entry *e) COVERAGE_INC(igmp_snooping_learned); e->tag = tag_create_random(); - return old_tag ? old_tag : make_unknown_mdb_tag(mdb, e->group, e->vlan); + return old_tag ? old_tag : make_unknown_mdb_tag(mdb, &e->group, e->vlan); } /* Looks up group 'dst' for VLAN 'vlan' in 'mdb' and returns the associated @@ -275,12 +287,42 @@ igmp_mdb_lookup(const struct igmp_mdb *mdb, ovs_be32 dst, * rarely that we revalidate every flow when it changes. */ return NULL; } else { - struct mdb_entry *e = mdb_entry_lookup(mdb, dst, vlan); + struct mdb_ip ip; + struct mdb_entry *e; + ip.u.ip4 = dst; + e = mdb_entry_lookup(mdb, &ip, vlan); + + ovs_assert(e == NULL || e->tag != 0); + if (tag) { + /* Tag either the learned port or the lack thereof. */ + *tag |= e ? e->tag : make_unknown_mdb_tag(mdb, &ip, vlan); + } + return e; + } +} + +struct mdb_entry * +igmp6_mdb_lookup(const struct igmp_mdb *mdb, struct in6_addr *dst, + uint16_t vlan, tag_type *tag) +{ + if (!ipv6_addr_is_multicast(dst)) { + /* No tag because the treatment of multicast destinations never + * changes. */ + return NULL; + } else if (!is_learning_vlan(mdb, vlan)) { + /* We don't tag this property. The set of learning VLANs changes so + * rarely that we revalidate every flow when it changes. */ + return NULL; + } else { + struct mdb_ip ip; + struct mdb_entry *e; + ip.u.ip6 = *dst; + e = mdb_entry_lookup(mdb, &ip, vlan); ovs_assert(e == NULL || e->tag != 0); if (tag) { /* Tag either the learned port or the lack thereof. */ - *tag |= e ? e->tag : make_unknown_mdb_tag(mdb, dst, vlan); + *tag |= e ? e->tag : make_unknown_mdb_tag(mdb, &ip, vlan); } return e; } @@ -313,7 +355,7 @@ igmp_mdb_flush(struct igmp_mdb *mdb, struct tag_set *tags) } void -igmp_mdb_delete(struct igmp_mdb *mdb, ovs_be32 group, int vlan) +igmp_mdb_delete(struct igmp_mdb *mdb, const struct mdb_ip *group, uint16_t vlan) { struct mdb_entry *mdb_entry; diff --git a/lib/igmp-snooping.h b/lib/igmp-snooping.h index d1f8fbb..72f00c6 100644 --- a/lib/igmp-snooping.h +++ b/lib/igmp-snooping.h @@ -37,7 +37,7 @@ struct mdb_entry { struct hmap_node hmap_node; /* Node in a mdb hmap. */ struct list lru_node; /* Element in 'lrus' list. */ time_t expires; /* Expiration time. */ - ovs_be32 group; /* Known multicast group. */ + struct mdb_ip group; /* Known multicast group. */ uint16_t vlan; /* VLAN tag. */ tag_type tag; /* Tag for this entry. */ @@ -85,15 +85,18 @@ void igmp_mdb_set_max_entries(struct igmp_mdb *, size_t max_entries); bool igmp_may_snoop(const struct igmp_mdb *mdb, const ovs_be32 dst, uint16_t vlan); -struct mdb_entry *igmp_mdb_insert(struct igmp_mdb *mdb, ovs_be32 grp, - uint16_t vlan); -void igmp_mdb_delete(struct igmp_mdb *mdb, ovs_be32 group, int vlan); +struct mdb_entry *igmp_mdb_insert(struct igmp_mdb *mdb, const struct mdb_ip *grp, uint16_t vlan); +void igmp_mdb_delete(struct igmp_mdb *mdb, const struct mdb_ip *group, uint16_t vlan); tag_type igmp_mdb_changed(struct igmp_mdb *mdb, struct mdb_entry *e); struct mdb_entry * igmp_mdb_lookup(const struct igmp_mdb *mdb, ovs_be32 dst, uint16_t vlan, tag_type *tag); +struct mdb_entry * +igmp6_mdb_lookup(const struct igmp_mdb *mdb, struct in6_addr *dst, + uint16_t vlan, tag_type *tag); + /* Flushing. */ void igmp_mdb_expire(struct igmp_mdb *mdb, struct mdb_entry *e); diff --git a/lib/packets.h b/lib/packets.h index 5c4995a..bab0c26 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -20,6 +20,7 @@ #include <inttypes.h> #include <sys/types.h> #include <netinet/in.h> +#include <netinet/icmp6.h> #include <stdint.h> #include <string.h> #include "compiler.h" @@ -541,6 +542,11 @@ BUILD_ASSERT_DECL(ARP_ETH_HEADER_LEN == sizeof(struct arp_eth_header)); #define IPV6_SCAN_FMT "%46[0123456789abcdefABCDEF:.]" #define IPV6_SCAN_LEN 46 +#define ICMPV6_MGM_QUERY 130 +#define ICMPV6_MGM_REPORT 131 +#define ICMPV6_MGM_REDUCTION 132 +#define ICMPV6_MLD2_REPORT 143 + 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 } } } @@ -555,6 +561,11 @@ static inline bool ipv6_addr_equals(const struct in6_addr *a, #endif } +static inline bool ipv6_addr_is_multicast(const struct in6_addr *addr) +{ + return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000); +} + static inline bool ipv6_mask_is_any(const struct in6_addr *mask) { return ipv6_addr_equals(mask, &in6addr_any); } diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index ec44e41..bfb0a3b 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -7048,9 +7048,10 @@ update_mdb_table(struct ofproto_dpif *ofproto, return; } - if (ntohs(flow->tp_src) == IGMP_HOST_MEMBERSHIP_REPORT || - ntohs(flow->tp_src) == IGMPV2_HOST_MEMBERSHIP_REPORT) { - mdb_entry = igmp_mdb_insert(ofproto->mdb, flow->igmp_group, vlan); + switch (ntohs(flow->tp_src)) { + case IGMP_HOST_MEMBERSHIP_REPORT: + case IGMPV2_HOST_MEMBERSHIP_REPORT: + mdb_entry = igmp_mdb_insert(ofproto->mdb, &flow->igmp_group, vlan); if (mdb_entry_is_new(mdb_entry) || mdb_entry->port.p != in_bundle) { /* The log messages here could actually be useful in debugging, @@ -7058,15 +7059,23 @@ update_mdb_table(struct ofproto_dpif *ofproto, static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); VLOG_DBG_RL(&rl, "bridge %s: learned that "IP_FMT" is " "on port %s in VLAN %d", - ofproto->up.name, IP_ARGS(flow->igmp_group), + ofproto->up.name, IP_ARGS(flow->igmp_group.u.ip4), in_bundle->name, vlan); mdb_entry->port.p = in_bundle; tag_set_add(&ofproto->backer->revalidate_set, igmp_mdb_changed(ofproto->mdb, mdb_entry)); } - } else if (ntohs(flow->tp_src) == IGMP_HOST_LEAVE_MESSAGE) { - igmp_mdb_delete(ofproto->mdb, flow->igmp_group, vlan); + break; + case IGMP_HOST_LEAVE_MESSAGE: + igmp_mdb_delete(ofproto->mdb, &flow->igmp_group, vlan); + break; + case ICMPV6_MGM_REPORT: + case ICMPV6_MGM_QUERY: + break; + case ICMPV6_MGM_REDUCTION: + igmp_mdb_delete(ofproto->mdb, &flow->igmp_group, vlan); + break; } } @@ -7230,15 +7239,19 @@ xlate_normal(struct action_xlate_ctx *ctx) /* Learn source MAC. */ if (ctx->may_learn) { if (eth_addr_is_multicast(ctx->flow.dl_dst) && - (ctx->flow.nw_proto == IPPROTO_IGMP)) + (ctx->flow.nw_proto == IPPROTO_IGMP || ctx->flow.nw_proto == IPPROTO_ICMPV6)) update_mdb_table(ctx->ofproto, &ctx->flow, vlan, in_bundle); else update_learning_table(ctx->ofproto, &ctx->flow, vlan, in_bundle); } if (eth_addr_is_multicast(ctx->flow.dl_dst)) { - mdb_entry = igmp_mdb_lookup(ctx->ofproto->mdb, ctx->flow.nw_dst, vlan, - &ctx->tags); + if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) + mdb_entry = igmp_mdb_lookup(ctx->ofproto->mdb, ctx->flow.nw_dst, vlan, + &ctx->tags); + else + mdb_entry = igmp6_mdb_lookup(ctx->ofproto->mdb, &ctx->flow.ipv6_dst, vlan, + &ctx->tags); if (mdb_entry) { if (mdb_entry->port.p != in_bundle) { xlate_report(ctx, "forwarding to learned port"); @@ -7566,10 +7579,18 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED, ds_put_cstr(&ds, " port VLAN MAC Age\n"); LIST_FOR_EACH (e, lru_node, &ofproto->mdb->lrus) { struct ofbundle *bundle = e->port.p; - ds_put_format(&ds, "%5d %4d "IP_FMT" %3d\n", - ofbundle_get_a_port(bundle)->odp_port, - e->vlan, IP_ARGS(e->group), - mdb_entry_age(ofproto->mdb, e)); + char addr_str[INET6_ADDRSTRLEN]; + if (e->group.proto == htons(ETH_TYPE_IP)) + ds_put_format(&ds, "%5d %4d "IP_FMT" %3d\n", + ofbundle_get_a_port(bundle)->odp_port, + e->vlan, IP_ARGS(e->group.u.ip4), + mdb_entry_age(ofproto->mdb, e)); + else + ds_put_format(&ds, "%5d %4d %s %3d\n", + ofbundle_get_a_port(bundle)->odp_port, + e->vlan, + inet_ntop(AF_INET6, &e->group.u.ip6, addr_str, INET6_ADDRSTRLEN), + mdb_entry_age(ofproto->mdb, e)); } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); @@ -7614,10 +7635,19 @@ ofproto_unixctl_mdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED, ds_put_cstr(&ds, " port VLAN GROUP Age\n"); LIST_FOR_EACH (e, lru_node, &ofproto->mdb->lrus) { struct ofbundle *bundle = e->port.p; - ds_put_format(&ds, "%5d %4d "IP_FMT" %3d\n", - ofbundle_get_a_port(bundle)->odp_port, - e->vlan, IP_ARGS(e->group), - mdb_entry_age(ofproto->mdb, e)); + char addr_str[INET6_ADDRSTRLEN]; + if (e->group.proto == htons(ETH_TYPE_IP)) + ds_put_format(&ds, "%5d %4d "IP_FMT" %3d\n", + ofbundle_get_a_port(bundle)->odp_port, + e->vlan, IP_ARGS(e->group.u.ip4), + mdb_entry_age(ofproto->mdb, e)); + else + ds_put_format(&ds, "%5d %4d %s %3d\n", + ofbundle_get_a_port(bundle)->odp_port, + e->vlan, + inet_ntop(AF_INET6, &e->group.u.ip6, addr_str, INET6_ADDRSTRLEN), + mdb_entry_age(ofproto->mdb, e)); + } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); -- 1.7.7.6 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev