summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThadeu Lima de Souza Cascardo <cascardo@redhat.com>2015-06-17 14:12:20 -0300
committerBen Pfaff <blp@nicira.com>2015-06-17 11:09:33 -0700
commite3102e42ef2afdeecdbdaf95f8dd119df3e1de7e (patch)
treec3f0bc1d94a4d28ed94fcfe7e52d9fcf28aa60c1
parentd29f137b65e8051f7da0eacf1901bcb7245b6b4e (diff)
downloadopenvswitch-e3102e42ef2afdeecdbdaf95f8dd119df3e1de7e.tar.gz
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 <cascardo@redhat.com> Acked-by: Flavio Leitner <fbl@sysclose.org> Signed-off-by: Ben Pfaff <blp@nicira.com>
-rw-r--r--NEWS2
-rw-r--r--lib/mcast-snooping.c52
-rw-r--r--lib/mcast-snooping.h5
-rw-r--r--lib/packets.h26
-rw-r--r--ofproto/ofproto-dpif-xlate.c19
-rw-r--r--vswitchd/vswitch.xml2
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 <time.h>
+#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.
<column name="mcast_snooping_enable">
Enable multicast snooping on the bridge. For now, the default