Skip to content

Commit

Permalink
ofproto-dpif-xlate: Adding IGMP/MLD checksum verification
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
chaudron authored and blp committed Dec 22, 2016
1 parent d762337 commit 46445c6
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 0 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ Dustin Lundquist [email protected]
Ed Maste [email protected]
Ed Swierk [email protected]
Edouard Bourguignon [email protected]
Eelco Chaudron [email protected]
Esteban Rodriguez Betancourt [email protected]
Aymerich Edward [email protected]
Edward Tomasz Napierała [email protected]
Expand Down
20 changes: 20 additions & 0 deletions lib/packets.c
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,26 @@ 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_continue(partial, &ip6->ip6_src, sizeof ip6->ip6_src);
partial = csum_continue(partial, &ip6->ip6_dst, sizeof ip6->ip6_dst);
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
Expand Down
2 changes: 2 additions & 0 deletions lib/packets.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
27 changes: 27 additions & 0 deletions ofproto/ofproto-dpif-xlate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
37 changes: 37 additions & 0 deletions tests/mcast-snooping.at
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 46445c6

Please sign in to comment.