Skip to content

Commit

Permalink
conntrack: Fix ICMPv4 error data L4 length check.
Browse files Browse the repository at this point in the history
The ICMPv4 error data L4 length check was found to be too strict for TCP,
expecting a minimum of 20 rather than 8 bytes.  This worked by
hapenstance for other inner protocols.  The approach is to explicitly
handle the ICMPv4 error data L4 length check and to do this for all
supported inner protocols in the same way.  Making the code common
between protocols also allows the existing ICMPv4 related UDP tests to
cover TCP and ICMP inner protocol cases.
Note that ICMPv6 does not have an 8 byte limit for error L4 data.

Fixes: a489b16 ("conntrack: New userspace connection tracker.")
CC: Daniele Di Proietto <diproiettod@ovn.org>
Reported-at: https://mail.openvswitch.org/pipermail/ovs-dev/2019-August/361949.html
Reported-by: Vishal Deep Ajmera <vishal.deep.ajmera@ericsson.com>
Signed-off-by: Vishal Deep Ajmera <vishal.deep.ajmera@ericsson.com>
Co-authored-by: Vishal Deep Ajmera <vishal.deep.ajmera@ericsson.com>
Signed-off-by: Darrell Ball <dlu998@gmail.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
  • Loading branch information
2 people authored and blp committed Sep 30, 2019
1 parent 05b4f29 commit a01710e
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 15 deletions.
35 changes: 20 additions & 15 deletions lib/conntrack.c
Expand Up @@ -664,11 +664,12 @@ check_l4_icmp6(const struct conn_key *key, const void *data, size_t size,
}

static inline bool
extract_l4_tcp(struct conn_key *key, const void *data, size_t size)
extract_l4_tcp(struct conn_key *key, const void *data, size_t size,
size_t *chk_len)
{
const struct tcp_header *tcp = data;

if (OVS_UNLIKELY(size < TCP_HEADER_LEN)) {
if (OVS_UNLIKELY(size < (chk_len ? *chk_len : TCP_HEADER_LEN))) {
return false;
}

Expand All @@ -680,11 +681,12 @@ extract_l4_tcp(struct conn_key *key, const void *data, size_t size)
}

static inline bool
extract_l4_udp(struct conn_key *key, const void *data, size_t size)
extract_l4_udp(struct conn_key *key, const void *data, size_t size,
size_t *chk_len)
{
const struct udp_header *udp = data;

if (OVS_UNLIKELY(size < UDP_HEADER_LEN)) {
if (OVS_UNLIKELY(size < (chk_len ? *chk_len : UDP_HEADER_LEN))) {
return false;
}

Expand All @@ -696,7 +698,8 @@ extract_l4_udp(struct conn_key *key, const void *data, size_t size)
}

static inline bool extract_l4(struct conn_key *key, const void *data,
size_t size, bool *related, const void *l3);
size_t size, bool *related, const void *l3,
size_t *chk_len);

static uint8_t
reverse_icmp_type(uint8_t type)
Expand Down Expand Up @@ -728,11 +731,11 @@ reverse_icmp_type(uint8_t type)
* possible */
static inline int
extract_l4_icmp(struct conn_key *key, const void *data, size_t size,
bool *related)
bool *related, size_t *chk_len)
{
const struct icmp_header *icmp = data;

if (OVS_UNLIKELY(size < ICMP_HEADER_LEN)) {
if (OVS_UNLIKELY(size < (chk_len ? *chk_len : ICMP_HEADER_LEN))) {
return false;
}

Expand Down Expand Up @@ -783,8 +786,9 @@ extract_l4_icmp(struct conn_key *key, const void *data, size_t size,
key->src = inner_key.src;
key->dst = inner_key.dst;
key->nw_proto = inner_key.nw_proto;
size_t check_len = ICMP_ERROR_DATA_L4_LEN;

ok = extract_l4(key, l4, tail - l4, NULL, l3);
ok = extract_l4(key, l4, tail - l4, NULL, l3, &check_len);
if (ok) {
conn_key_reverse(key);
*related = true;
Expand Down Expand Up @@ -872,7 +876,7 @@ extract_l4_icmp6(struct conn_key *key, const void *data, size_t size,
key->dst = inner_key.dst;
key->nw_proto = inner_key.nw_proto;

ok = extract_l4(key, l4, tail - l4, NULL, l3);
ok = extract_l4(key, l4, tail - l4, NULL, l3, NULL);
if (ok) {
conn_key_reverse(key);
*related = true;
Expand All @@ -897,21 +901,22 @@ extract_l4_icmp6(struct conn_key *key, const void *data, size_t size,
* an ICMP or ICMP6 header. *
*
* If 'related' is NULL, it means that we're already parsing a header nested
* in an ICMP error. In this case, we skip checksum and length validation. */
* in an ICMP error. In this case, we skip the checksum and some length
* validations. */
static inline bool
extract_l4(struct conn_key *key, const void *data, size_t size, bool *related,
const void *l3)
const void *l3, size_t *chk_len)
{
if (key->nw_proto == IPPROTO_TCP) {
return (!related || check_l4_tcp(key, data, size, l3))
&& extract_l4_tcp(key, data, size);
&& extract_l4_tcp(key, data, size, chk_len);
} else if (key->nw_proto == IPPROTO_UDP) {
return (!related || check_l4_udp(key, data, size, l3))
&& extract_l4_udp(key, data, size);
&& extract_l4_udp(key, data, size, chk_len);
} else if (key->dl_type == htons(ETH_TYPE_IP)
&& key->nw_proto == IPPROTO_ICMP) {
return (!related || check_l4_icmp(data, size))
&& extract_l4_icmp(key, data, size, related);
&& extract_l4_icmp(key, data, size, related, chk_len);
} else if (key->dl_type == htons(ETH_TYPE_IPV6)
&& key->nw_proto == IPPROTO_ICMPV6) {
return (!related || check_l4_icmp6(key, data, size, l3))
Expand Down Expand Up @@ -982,7 +987,7 @@ conn_key_extract(struct conntrack *ct, struct dp_packet *pkt, ovs_be16 dl_type,

if (ok) {
if (extract_l4(&ctx->key, l4, dp_packet_l4_size(pkt),
&ctx->related, l3)) {
&ctx->related, l3, NULL)) {
ctx->hash = conn_key_hash(&ctx->key, ct->hash_basis);
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions lib/packets.h
Expand Up @@ -656,6 +656,9 @@ struct icmp_header {
};
BUILD_ASSERT_DECL(ICMP_HEADER_LEN == sizeof(struct icmp_header));

/* ICMPV4 */
#define ICMP_ERROR_DATA_L4_LEN 8

#define IGMP_HEADER_LEN 8
struct igmp_header {
uint8_t igmp_type;
Expand Down

0 comments on commit a01710e

Please sign in to comment.