When IGMP or MLD packets arrive their content is used without the checksum
being verified. With this change the checksum is verified, and the packet
is not used for multicast snooping on failure.

Signed-off-by: Eelco Chaudron <echau...@redhat.com>
---
 lib/packets.c                | 28 ++++++++++++++++++++++++++++
 lib/packets.h                |  2 ++
 ofproto/ofproto-dpif-xlate.c | 27 +++++++++++++++++++++++++++
 tests/mcast-snooping.at      | 37 +++++++++++++++++++++++++++++++++++++
 4 files changed, 94 insertions(+)

diff --git a/lib/packets.c b/lib/packets.c
index ae81f46..8075dda 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -1474,6 +1474,34 @@ packet_csum_pseudoheader6(const struct 
ovs_16aligned_ip6_hdr *ip6)
 
     return partial;
 }
+
+/* Calculate the IPv6 upper layer checksum according to RFC2460. We pass the
+   ip6_nxt and ip6_plen values, so it will also work if extension headers
+   are present. */
+uint16_t
+packet_csum_upperlayer6(const struct ovs_16aligned_ip6_hdr *ip6,
+                        const void *data, uint8_t l4_protocol,
+                        uint16_t l4_size)
+{
+    uint32_t partial = 0;
+
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[0])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[1])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[2])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[3])));
+
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[0])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[1])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[2])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[3])));
+
+    partial = csum_add16(partial, htons(l4_protocol));
+    partial = csum_add16(partial, htons(l4_size));
+
+    partial = csum_continue(partial, data, l4_size);
+
+    return csum_finish(partial);
+}
 #endif
 
 void
diff --git a/lib/packets.h b/lib/packets.h
index 21bd35c..0bb6284 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -840,6 +840,8 @@ struct icmp6_header {
 BUILD_ASSERT_DECL(ICMP6_HEADER_LEN == sizeof(struct icmp6_header));
 
 uint32_t packet_csum_pseudoheader6(const struct ovs_16aligned_ip6_hdr *);
+uint16_t packet_csum_upperlayer6(const struct ovs_16aligned_ip6_hdr *,
+                                 const void *, uint8_t, uint16_t);
 
 /* Neighbor Discovery option field.
  * ND options are always a multiple of 8 bytes in size. */
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index d0f9a33..65921f0 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -30,6 +30,7 @@
 #include "cfm.h"
 #include "connmgr.h"
 #include "coverage.h"
+#include "csum.h"
 #include "dp-packet.h"
 #include "dpif.h"
 #include "in-band.h"
@@ -2021,9 +2022,20 @@ update_mcast_snooping_table4__(const struct xbridge 
*xbridge,
     OVS_REQ_WRLOCK(ms->rwlock)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 30);
+    const struct igmp_header *igmp;
     int count;
+    size_t offset;
     ovs_be32 ip4 = flow->igmp_group_ip4;
 
+    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
+    igmp = dp_packet_at(packet, offset, IGMP_HEADER_LEN);
+    if (!igmp || csum(igmp, dp_packet_l4_size(packet)) != 0) {
+        VLOG_DBG_RL(&rl, "bridge %s: multicast snooping received bad IGMP "
+                    "checksum on port %s in VLAN %d",
+                    xbridge->name, in_xbundle->name, vlan);
+        return;
+    }
+
     switch (ntohs(flow->tp_src)) {
     case IGMP_HOST_MEMBERSHIP_REPORT:
     case IGMPV2_HOST_MEMBERSHIP_REPORT:
@@ -2069,7 +2081,22 @@ update_mcast_snooping_table6__(const struct xbridge 
*xbridge,
     OVS_REQ_WRLOCK(ms->rwlock)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 30);
+    const struct mld_header *mld;
     int count;
+    size_t offset;
+
+    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
+    mld = dp_packet_at(packet, offset, MLD_HEADER_LEN);
+
+    if (!mld ||
+        packet_csum_upperlayer6(dp_packet_l3(packet),
+                                mld, IPPROTO_ICMPV6,
+                                dp_packet_l4_size(packet)) != 0) {
+        VLOG_DBG_RL(&rl, "bridge %s: multicast snooping received bad MLD "
+                    "checksum on port %s in VLAN %d",
+                    xbridge->name, in_xbundle->name, vlan);
+        return;
+    }
 
     switch (ntohs(flow->tp_src)) {
     case MLD_QUERY:
diff --git a/tests/mcast-snooping.at b/tests/mcast-snooping.at
index 9cceace..c03aba3 100644
--- a/tests/mcast-snooping.at
+++ b/tests/mcast-snooping.at
@@ -63,5 +63,42 @@ AT_CHECK([cat p2.pcap.txt], [0], [dnl
 
01005e5e0101aa55aa550001810006bd08004500001c00000000401180710a000001ef5e010100001f400008e63d
 ])
 
+# Clear the mdb, send a IGMP packet with invalid checksum and make sure it
+# does not end up in the mdb.
+AT_CHECK([ovs-appctl mdb/flush br0], [0], [dnl
+table successfully flushed
+])
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p2 \
+'01005e0000015c8a38552552810006bd080046c000240000000001027f00ac111901e0000001940400001164ec1000000000027d000000000000000000000000'])
+
+AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl
+ port  VLAN  GROUP                Age
+])
+
+
+# First send a valid packet to make sure it populates the mdb. Than Clear
+# the mdb, send a MLD packet with invalid checksum and make sure it does
+# not end up in the mdb.
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p2 \
+'3333ff0e4c67000c290e4c6786dd600000000020000100000000000000000000000000000000ff0200000000000000000001ff0e4c673a000502000001008300e7b800000000ff0200000000000000000001ff0e4c67'])
+
+AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl
+ port  VLAN  GROUP                Age
+    2     0  ff02::1:ff0e:4c67           0
+])
+
+AT_CHECK([ovs-appctl mdb/flush br0], [0], [dnl
+table successfully flushed
+])
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p2 \
+'3333ff0e4c67000c290e4c6786dd600000000020000100000000000000000000000000000000ff0200000000000000000001ff0e4c673a000502000001008300e7b000000000ff0200000000000000000001ff0e4c67'])
+
+AT_CHECK([ovs-appctl mdb/show br0], [0], [dnl
+ port  VLAN  GROUP                Age
+])
+
 OVS_VSWITCHD_STOP
 AT_CLEANUP
-- 
2.9.3

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to