2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 06:15:47 +00:00

flow: Count and dump invalid IP packets.

Skipping further processing of invalid IP packets helps avoid crashes
but it does not help to figure out if the malformed packets are still
present on the network.

Add coverage counters for IPv4 and IPv6 sanity checks so that we know
there are some invalid packets.

Dump such whole packets in debug mode.

Signed-off-by: David Marchand <david.marchand@redhat.com>
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Acked-by: Flavio Leitner <fbl@sysclose.org>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
David Marchand
2021-04-16 14:06:31 +02:00
committed by Ilya Maximets
parent 6545977cee
commit 15329b728b

View File

@@ -44,8 +44,15 @@
#include "openvswitch/nsh.h"
#include "ovs-router.h"
#include "lib/netdev-provider.h"
#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(flow);
COVERAGE_DEFINE(flow_extract);
COVERAGE_DEFINE(miniflow_extract_ipv4_pkt_len_error);
COVERAGE_DEFINE(miniflow_extract_ipv4_pkt_too_short);
COVERAGE_DEFINE(miniflow_extract_ipv6_pkt_len_error);
COVERAGE_DEFINE(miniflow_extract_ipv6_pkt_too_short);
COVERAGE_DEFINE(miniflow_malloc);
/* U64 indices for segmented flow classification. */
@@ -645,17 +652,20 @@ ipv4_sanity_check(const struct ip_header *nh, size_t size,
uint16_t tot_len;
if (OVS_UNLIKELY(size < IP_HEADER_LEN)) {
COVERAGE_INC(miniflow_extract_ipv4_pkt_too_short);
return false;
}
ip_len = IP_IHL(nh->ip_ihl_ver) * 4;
if (OVS_UNLIKELY(ip_len < IP_HEADER_LEN || size < ip_len)) {
COVERAGE_INC(miniflow_extract_ipv4_pkt_len_error);
return false;
}
tot_len = ntohs(nh->ip_tot_len);
if (OVS_UNLIKELY(tot_len > size || ip_len > tot_len ||
size - tot_len > UINT16_MAX)) {
COVERAGE_INC(miniflow_extract_ipv4_pkt_len_error);
return false;
}
@@ -686,21 +696,41 @@ ipv6_sanity_check(const struct ovs_16aligned_ip6_hdr *nh, size_t size)
uint16_t plen;
if (OVS_UNLIKELY(size < sizeof *nh)) {
COVERAGE_INC(miniflow_extract_ipv6_pkt_too_short);
return false;
}
plen = ntohs(nh->ip6_plen);
if (OVS_UNLIKELY(plen + IPV6_HEADER_LEN > size)) {
COVERAGE_INC(miniflow_extract_ipv6_pkt_len_error);
return false;
}
if (OVS_UNLIKELY(size - (plen + IPV6_HEADER_LEN) > UINT16_MAX)) {
COVERAGE_INC(miniflow_extract_ipv6_pkt_len_error);
return false;
}
return true;
}
static void
dump_invalid_packet(struct dp_packet *packet, const char *reason)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
struct ds ds = DS_EMPTY_INITIALIZER;
size_t size;
if (VLOG_DROP_DBG(&rl)) {
return;
}
size = dp_packet_size(packet);
ds_put_hex_dump(&ds, dp_packet_data(packet), size, 0, false);
VLOG_DBG("invalid packet for %s: port %"PRIu32", size %"PRIuSIZE"\n%s",
reason, packet->md.in_port.odp_port, size, ds_cstr(&ds));
ds_destroy(&ds);
}
/* Initializes 'dst' from 'packet' and 'md', taking the packet type into
* account. 'dst' must have enough space for FLOW_U64S * 8 bytes.
*
@@ -850,6 +880,9 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
uint16_t tot_len;
if (OVS_UNLIKELY(!ipv4_sanity_check(nh, size, &ip_len, &tot_len))) {
if (OVS_UNLIKELY(VLOG_IS_DBG_ENABLED())) {
dump_invalid_packet(packet, "ipv4_sanity_check");
}
goto out;
}
dp_packet_set_l2_pad_size(packet, size - tot_len);
@@ -880,6 +913,9 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
uint16_t plen;
if (OVS_UNLIKELY(!ipv6_sanity_check(nh, size))) {
if (OVS_UNLIKELY(VLOG_IS_DBG_ENABLED())) {
dump_invalid_packet(packet, "ipv6_sanity_check");
}
goto out;
}
data_pull(&data, &size, sizeof *nh);
@@ -1114,6 +1150,9 @@ parse_tcp_flags(struct dp_packet *packet)
uint16_t tot_len;
if (OVS_UNLIKELY(!ipv4_sanity_check(nh, size, &ip_len, &tot_len))) {
if (OVS_UNLIKELY(VLOG_IS_DBG_ENABLED())) {
dump_invalid_packet(packet, "ipv4_sanity_check");
}
return 0;
}
dp_packet_set_l2_pad_size(packet, size - tot_len);
@@ -1127,6 +1166,9 @@ parse_tcp_flags(struct dp_packet *packet)
uint16_t plen;
if (OVS_UNLIKELY(!ipv6_sanity_check(nh, size))) {
if (OVS_UNLIKELY(VLOG_IS_DBG_ENABLED())) {
dump_invalid_packet(packet, "ipv6_sanity_check");
}
return 0;
}
data_pull(&data, &size, sizeof *nh);