From e3102e42ef2afdeecdbdaf95f8dd119df3e1de7e Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Wed, 17 Jun 2015 14:12:20 -0300 Subject: [PATCH] Add IGMPv3 support. Support IGMPv3 messages with multiple records. Make sure all IGMPv3 messages go through slow path, since they may carry multiple multicast addresses, unlike IGMPv2. Tests done: * multiple addresses in IGMPv3 report are inserted in mdb; * address is removed from IGMPv3 if record is INCLUDE_MODE; * reports sent on a burst with same flow all go to userspace; * IGMPv3 reports go to mrouters, i.e., ports that have issued a query. Signed-off-by: Thadeu Lima de Souza Cascardo Acked-by: Flavio Leitner Signed-off-by: Ben Pfaff --- NEWS | 2 +- lib/mcast-snooping.c | 52 ++++++++++++++++++++++++++++++++++++ lib/mcast-snooping.h | 5 ++++ lib/packets.h | 26 ++++++++++++++++++ ofproto/ofproto-dpif-xlate.c | 19 ++++++++++--- vswitchd/vswitch.xml | 2 +- 6 files changed, 100 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 90d9a2934..43461b27c 100644 --- a/NEWS +++ b/NEWS @@ -87,7 +87,7 @@ Post-v2.3.0 with Docker, the wrapper script will be retired. - Added support for DPDK Tunneling. VXLAN, GRE, and Geneve are supported protocols. This is generic tunneling mechanism for userspace datapath. - - Support for multicast snooping (IGMPv1 and IGMPv2) + - Support for multicast snooping (IGMPv1, IGMPv2 and IGMPv3) - Support for Linux kernels up to 4.0.x - The documentation now use the term 'destination' to mean one of syslog, console or file for vlog logging instead of the previously used term diff --git a/lib/mcast-snooping.c b/lib/mcast-snooping.c index c3ffd6b77..7b927aa27 100644 --- a/lib/mcast-snooping.c +++ b/lib/mcast-snooping.c @@ -69,6 +69,7 @@ mcast_snooping_is_membership(ovs_be16 igmp_type) switch (ntohs(igmp_type)) { case IGMP_HOST_MEMBERSHIP_REPORT: case IGMPV2_HOST_MEMBERSHIP_REPORT: + case IGMPV3_HOST_MEMBERSHIP_REPORT: case IGMP_HOST_LEAVE_MESSAGE: return true; } @@ -416,6 +417,57 @@ mcast_snooping_add_group(struct mcast_snooping *ms, ovs_be32 ip4, return learned; } +int +mcast_snooping_add_report(struct mcast_snooping *ms, + const struct dp_packet *p, + uint16_t vlan, void *port) +{ + ovs_be32 ip4; + size_t offset; + const struct igmpv3_header *igmpv3; + const struct igmpv3_record *record; + int count = 0; + int ngrp; + + offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p); + igmpv3 = dp_packet_at(p, offset, IGMPV3_HEADER_LEN); + if (!igmpv3) { + return 0; + } + ngrp = ntohs(igmpv3->ngrp); + offset += IGMPV3_HEADER_LEN; + while (ngrp--) { + bool ret; + record = dp_packet_at(p, offset, sizeof(struct igmpv3_record)); + if (!record) { + break; + } + /* Only consider known record types. */ + if (record->type < IGMPV3_MODE_IS_INCLUDE + || record->type > IGMPV3_BLOCK_OLD_SOURCES) { + continue; + } + ip4 = get_16aligned_be32(&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, ip4, vlan, port); + } else { + ret = mcast_snooping_add_group(ms, ip4, vlan, port); + } + if (ret) { + count++; + } + offset += sizeof(*record) + + ntohs(record->nsrcs) * sizeof(ovs_be32) + record->aux_len; + } + return count; +} + bool mcast_snooping_leave_group(struct mcast_snooping *ms, ovs_be32 ip4, uint16_t vlan, void *port) diff --git a/lib/mcast-snooping.h b/lib/mcast-snooping.h index 979b2aa60..f4bc8fb03 100644 --- a/lib/mcast-snooping.h +++ b/lib/mcast-snooping.h @@ -20,6 +20,7 @@ #define MCAST_SNOOPING_H 1 #include +#include "dp-packet.h" #include "hmap.h" #include "list.h" #include "ovs-atomic.h" @@ -181,6 +182,10 @@ mcast_snooping_lookup(const struct mcast_snooping *ms, ovs_be32 dip, bool mcast_snooping_add_group(struct mcast_snooping *ms, ovs_be32 ip4, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock); +int mcast_snooping_add_report(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, ovs_be32 ip4, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock); diff --git a/lib/packets.h b/lib/packets.h index e22267efc..63ad2ff04 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -540,12 +540,38 @@ struct igmp_header { }; BUILD_ASSERT_DECL(IGMP_HEADER_LEN == sizeof(struct igmp_header)); +#define IGMPV3_HEADER_LEN 8 +struct igmpv3_header { + uint8_t type; + uint8_t rsvr1; + ovs_be16 csum; + ovs_be16 rsvr2; + ovs_be16 ngrp; +}; +BUILD_ASSERT_DECL(IGMPV3_HEADER_LEN == sizeof(struct igmpv3_header)); + +#define IGMPV3_RECORD_LEN 8 +struct igmpv3_record { + uint8_t type; + uint8_t aux_len; + ovs_be16 nsrcs; + ovs_16aligned_be32 maddr; +}; +BUILD_ASSERT_DECL(IGMPV3_RECORD_LEN == sizeof(struct igmpv3_record)); + #define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* From RFC1112 */ #define IGMP_HOST_MEMBERSHIP_REPORT 0x12 /* Ditto */ #define IGMPV2_HOST_MEMBERSHIP_REPORT 0x16 /* V2 version of 0x12 */ #define IGMP_HOST_LEAVE_MESSAGE 0x17 #define IGMPV3_HOST_MEMBERSHIP_REPORT 0x22 /* V3 version of 0x12 */ +#define IGMPV3_MODE_IS_INCLUDE 1 +#define IGMPV3_MODE_IS_EXCLUDE 2 +#define IGMPV3_CHANGE_TO_INCLUDE_MODE 3 +#define IGMPV3_CHANGE_TO_EXCLUDE_MODE 4 +#define IGMPV3_ALLOW_NEW_SOURCES 5 +#define IGMPV3_BLOCK_OLD_SOURCES 6 + #define SCTP_HEADER_LEN 12 struct sctp_header { ovs_be16 sctp_src; diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index a0d13c265..5c1e63c56 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -1997,10 +1997,12 @@ 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) + 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 IGMP_HOST_MEMBERSHIP_REPORT: @@ -2027,6 +2029,14 @@ update_mcast_snooping_table__(const struct xbridge *xbridge, in_xbundle->name, vlan); } break; + case IGMPV3_HOST_MEMBERSHIP_REPORT: + if ((count = mcast_snooping_add_report(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; } } @@ -2035,7 +2045,8 @@ update_mcast_snooping_table__(const struct xbridge *xbridge, static void update_mcast_snooping_table(const struct xbridge *xbridge, const struct flow *flow, int vlan, - struct xbundle *in_xbundle) + struct xbundle *in_xbundle, + const struct dp_packet *packet) { struct mcast_snooping *ms = xbridge->ms; struct xlate_cfg *xcfg; @@ -2060,7 +2071,7 @@ 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); + vlan, in_xbundle, packet); } ovs_rwlock_unlock(&ms->rwlock); } @@ -2273,7 +2284,7 @@ xlate_normal(struct xlate_ctx *ctx) mcast_snooping_is_query(flow->tp_src)) { if (ctx->xin->may_learn) { update_mcast_snooping_table(ctx->xbridge, flow, vlan, - in_xbundle); + in_xbundle, ctx->xin->packet); } /* * IGMP packets need to take the slow path, in order to be diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 8a604744a..c43bfd1a7 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -940,7 +940,7 @@ Protocol (IGMP) traffic between hosts and multicast routers. The switch uses what IGMP snooping learns to forward multicast traffic only to interfaces that are connected to interested receivers. - Currently it supports IGMPv1 and IGMPv2 protocols. + Currently it supports IGMPv1, IGMPv2 and IGMPv3 protocols. Enable multicast snooping on the bridge. For now, the default -- 2.20.1