diff mbox

[ovs-dev] ofproto-dpif-xlate: Adding IGMP/MLD checksum verification

Message ID 12eec35fb81f5204362e68151e2cef2e7d5ca3c0.1481738052.git.echaudro@redhat.com
State Accepted
Headers show

Commit Message

Eelco Chaudron Dec. 14, 2016, 6:08 p.m. UTC
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 <echaudro@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(+)

Comments

Ben Pfaff Dec. 23, 2016, 5:07 p.m. UTC | #1
On Fri, Dec 23, 2016 at 10:31:19AM +0100, Eelco Chaudron wrote:
> On 22/12/16 17:14, Ben Pfaff wrote:
> >On Wed, Dec 14, 2016 at 07:08:27PM +0100, Eelco Chaudron wrote:
> >>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 <echaudro@redhat.com>
> >Thanks for the patch!  I applied it to master.  I folded in the
> >following simplification, which I believe to be correct and also passes
> >the test.  Please let me know if you see a problem.
> Thanks, your change is fine. I just mimicked the code above my change.

Oh, I hadn't noticed that.  I think it's best to adjust that code too.
I sent out a patch for review.

> >Also, I believe that this is your first contribution to Open vSwitch.
> >Thank you for helping, and welcome to the team!
> Yes this is my first patch, but I'm planning on doing a lot more next year!

Great!
Eelco Chaudron Jan. 3, 2017, 8:41 a.m. UTC | #2
On 22/12/16 17:14, Ben Pfaff wrote:
> On Wed, Dec 14, 2016 at 07:08:27PM +0100, Eelco Chaudron wrote:
>> 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 <echaudro@redhat.com>
> Thanks for the patch!  I applied it to master.
Hi Ben,

Can you also apply this to the 2.6 branch? I tried, and it applies clean.

Thanks,

Eelco
Ben Pfaff Jan. 4, 2017, 6:07 p.m. UTC | #3
On Tue, Jan 03, 2017 at 09:41:36AM +0100, Eelco Chaudron wrote:
> On 22/12/16 17:14, Ben Pfaff wrote:
> >On Wed, Dec 14, 2016 at 07:08:27PM +0100, Eelco Chaudron wrote:
> >>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 <echaudro@redhat.com>
> >Thanks for the patch!  I applied it to master.
> Hi Ben,
> 
> Can you also apply this to the 2.6 branch? I tried, and it applies clean.

Applied, thanks!
diff mbox

Patch

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