@@ -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
@@ -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. */
@@ -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:
@@ -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
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(+)