ospfd: introduce ospf_lsa_minlen[] (BZ#705)

This commit ports more packet checks to OSPFv2, in particular, LSA size
verification and Router-LSA link blocks verification.

* ospf_lsa.h: add LSA size macros
* ospf_packet.h: add struct ospf_ls_update
* ospf_packet.c
  * ospf_lsa_minlen[]: a direct equivalent of ospf6_lsa_minlen[]
  * ospf_router_lsa_links_examin(): new function, verifies trailing
    part of a Router-LSA
  * ospf_lsa_examin(): new function like ospf6_lsa_examin()
  * ospf_lsaseq_examin(): new function like ospf6_lsaseq_examin()
  * ospf_packet_examin(): add type-specific deeper level checks
This commit is contained in:
Denis Ovsienko 2012-02-17 16:20:50 +04:00 committed by David Lamparter
parent 2d8223c547
commit 4e31de792e
3 changed files with 267 additions and 1 deletions

View File

@ -153,6 +153,7 @@ struct router_lsa_link
};
/* OSPF Router-LSAs structure. */
#define OSPF_ROUTER_LSA_MIN_SIZE 16U /* w/1 link descriptor */
struct router_lsa
{
struct lsa_header header;
@ -170,6 +171,7 @@ struct router_lsa
};
/* OSPF Network-LSAs structure. */
#define OSPF_NETWORK_LSA_MIN_SIZE 8U /* w/1 router-ID */
struct network_lsa
{
struct lsa_header header;
@ -178,6 +180,7 @@ struct network_lsa
};
/* OSPF Summary-LSAs structure. */
#define OSPF_SUMMARY_LSA_MIN_SIZE 8U /* w/1 TOS metric block */
struct summary_lsa
{
struct lsa_header header;
@ -187,6 +190,7 @@ struct summary_lsa
};
/* OSPF AS-external-LSAs structure. */
#define OSPF_AS_EXTERNAL_LSA_MIN_SIZE 16U /* w/1 TOS forwarding block */
struct as_external_lsa
{
struct lsa_header header;

View File

@ -73,6 +73,24 @@ static const u_int16_t ospf_packet_minlen[] =
OSPF_LS_ACK_MIN_SIZE,
};
/* Minimum (besides OSPF_LSA_HEADER_SIZE) lengths for LSAs of particular
types, offset is the "LSA type" field. */
static const u_int16_t ospf_lsa_minlen[] =
{
0,
OSPF_ROUTER_LSA_MIN_SIZE,
OSPF_NETWORK_LSA_MIN_SIZE,
OSPF_SUMMARY_LSA_MIN_SIZE,
OSPF_SUMMARY_LSA_MIN_SIZE,
OSPF_AS_EXTERNAL_LSA_MIN_SIZE,
0,
OSPF_AS_EXTERNAL_LSA_MIN_SIZE,
0,
0,
0,
0,
};
/* OSPF authentication checking function */
static int
ospf_auth_type (struct ospf_interface *oi)
@ -2315,11 +2333,199 @@ ospf_check_sum (struct ospf_header *ospfh)
return 1;
}
/* Verify, that given link/TOS records are properly sized/aligned and match
Router-LSA "# links" and "# TOS" fields as specified in RFC2328 A.4.2. */
static unsigned
ospf_router_lsa_links_examin
(
struct router_lsa_link * link,
u_int16_t linkbytes,
const u_int16_t num_links
)
{
unsigned counted_links = 0, thislinklen;
while (linkbytes)
{
thislinklen = OSPF_ROUTER_LSA_LINK_SIZE + 4 * link->m[0].tos_count;
if (thislinklen > linkbytes)
{
if (IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: length error in link block #%u", __func__, counted_links);
return MSG_NG;
}
link = (struct router_lsa_link *)((caddr_t) link + thislinklen);
linkbytes -= thislinklen;
counted_links++;
}
if (counted_links != num_links)
{
if (IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: %u link blocks declared, %u present",
__func__, num_links, counted_links);
return MSG_NG;
}
return MSG_OK;
}
/* Verify, that the given LSA is properly sized/aligned (including type-specific
minimum length constraint). */
static unsigned
ospf_lsa_examin (struct lsa_header * lsah, const u_int16_t lsalen, const u_char headeronly)
{
unsigned ret;
struct router_lsa * rlsa;
if
(
lsah->type < OSPF_MAX_LSA &&
ospf_lsa_minlen[lsah->type] &&
lsalen < OSPF_LSA_HEADER_SIZE + ospf_lsa_minlen[lsah->type]
)
{
if (IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: undersized (%u B) %s",
__func__, lsalen, LOOKUP (ospf_lsa_type_msg, lsah->type));
return MSG_NG;
}
switch (lsah->type)
{
case OSPF_ROUTER_LSA:
/* RFC2328 A.4.2, LSA header + 4 bytes followed by N>=1 (12+)-byte link blocks */
if (headeronly)
{
ret = (lsalen - OSPF_LSA_HEADER_SIZE - OSPF_ROUTER_LSA_MIN_SIZE) % 4 ? MSG_NG : MSG_OK;
break;
}
rlsa = (struct router_lsa *) lsah;
ret = ospf_router_lsa_links_examin
(
(struct router_lsa_link *) rlsa->link,
lsalen - OSPF_LSA_HEADER_SIZE - 4, /* skip: basic header, "flags", 0, "# links" */
ntohs (rlsa->links) /* 16 bits */
);
break;
case OSPF_AS_EXTERNAL_LSA:
/* RFC2328 A.4.5, LSA header + 4 bytes followed by N>=1 12-bytes long blocks */
case OSPF_AS_NSSA_LSA:
/* RFC3101 C, idem */
ret = (lsalen - OSPF_LSA_HEADER_SIZE - OSPF_AS_EXTERNAL_LSA_MIN_SIZE) % 12 ? MSG_NG : MSG_OK;
break;
/* Following LSA types are considered OK length-wise as soon as their minimum
* length constraint is met and length of the whole LSA is a multiple of 4
* (basic LSA header size is already a multiple of 4). */
case OSPF_NETWORK_LSA:
/* RFC2328 A.4.3, LSA header + 4 bytes followed by N>=1 router-IDs */
case OSPF_SUMMARY_LSA:
case OSPF_ASBR_SUMMARY_LSA:
/* RFC2328 A.4.4, LSA header + 4 bytes followed by N>=1 4-bytes TOS blocks */
#ifdef HAVE_OPAQUE_LSA
case OSPF_OPAQUE_LINK_LSA:
case OSPF_OPAQUE_AREA_LSA:
case OSPF_OPAQUE_AS_LSA:
/* RFC5250 A.2, "some number of octets (of application-specific
* data) padded to 32-bit alignment." This is considered equivalent
* to 4-byte alignment of all other LSA types, see OSPF-ALIGNMENT.txt
* file for the detailed analysis of this passage. */
#endif
ret = lsalen % 4 ? MSG_NG : MSG_OK;
break;
default:
if (IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: unsupported LSA type 0x%02x", __func__, lsah->type);
return MSG_NG;
}
if (ret != MSG_OK && IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: alignment error in %s",
__func__, LOOKUP (ospf_lsa_type_msg, lsah->type));
return ret;
}
/* Verify if the provided input buffer is a valid sequence of LSAs. This
includes verification of LSA blocks length/alignment and dispatching
of deeper-level checks. */
static unsigned
ospf_lsaseq_examin
(
struct lsa_header *lsah, /* start of buffered data */
size_t length,
const u_char headeronly,
/* When declared_num_lsas is not 0, compare it to the real number of LSAs
and treat the difference as an error. */
const u_int32_t declared_num_lsas
)
{
u_int32_t counted_lsas = 0;
while (length)
{
u_int16_t lsalen;
if (length < OSPF_LSA_HEADER_SIZE)
{
if (IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: undersized (%zu B) trailing (#%u) LSA header",
__func__, length, counted_lsas);
return MSG_NG;
}
/* save on ntohs() calls here and in the LSA validator */
lsalen = ntohs (lsah->length);
if (lsalen < OSPF_LSA_HEADER_SIZE)
{
if (IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: malformed LSA header #%u, declared length is %u B",
__func__, counted_lsas, lsalen);
return MSG_NG;
}
if (headeronly)
{
/* less checks here and in ospf_lsa_examin() */
if (MSG_OK != ospf_lsa_examin (lsah, lsalen, 1))
{
if (IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: malformed header-only LSA #%u", __func__, counted_lsas);
return MSG_NG;
}
lsah = (struct lsa_header *) ((caddr_t) lsah + OSPF_LSA_HEADER_SIZE);
length -= OSPF_LSA_HEADER_SIZE;
}
else
{
/* make sure the input buffer is deep enough before further checks */
if (lsalen > length)
{
if (IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: anomaly in LSA #%u: declared length is %u B, buffered length is %zu B",
__func__, counted_lsas, lsalen, length);
return MSG_NG;
}
if (MSG_OK != ospf_lsa_examin (lsah, lsalen, 0))
{
if (IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: malformed LSA #%u", __func__, counted_lsas);
return MSG_NG;
}
lsah = (struct lsa_header *) ((caddr_t) lsah + lsalen);
length -= lsalen;
}
counted_lsas++;
}
if (declared_num_lsas && counted_lsas != declared_num_lsas)
{
if (IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: #LSAs declared (%u) does not match actual (%u)",
__func__, declared_num_lsas, counted_lsas);
return MSG_NG;
}
return MSG_OK;
}
/* Verify a complete OSPF packet for proper sizing/alignment. */
static unsigned
ospf_packet_examin (struct ospf_header * oh, const unsigned bytesonwire)
{
u_int16_t bytesdeclared;
unsigned ret;
struct ospf_ls_update * lsupd;
/* Length, 1st approximation. */
if (bytesonwire < OSPF_HEADER_SIZE)
@ -2353,7 +2559,59 @@ ospf_packet_examin (struct ospf_header * oh, const unsigned bytesonwire)
bytesdeclared, LOOKUP (ospf_packet_type_str, oh->type));
return MSG_NG;
}
return MSG_OK;
switch (oh->type)
{
case OSPF_MSG_HELLO:
/* RFC2328 A.3.2, packet header + OSPF_HELLO_MIN_SIZE bytes followed
by N>=0 router-IDs. */
ret = (bytesonwire - OSPF_HEADER_SIZE - OSPF_HELLO_MIN_SIZE) % 4 ? MSG_NG : MSG_OK;
break;
case OSPF_MSG_DB_DESC:
/* RFC2328 A.3.3, packet header + OSPF_DB_DESC_MIN_SIZE bytes followed
by N>=0 header-only LSAs. */
ret = ospf_lsaseq_examin
(
(struct lsa_header *) ((caddr_t) oh + OSPF_HEADER_SIZE + OSPF_DB_DESC_MIN_SIZE),
bytesonwire - OSPF_HEADER_SIZE - OSPF_DB_DESC_MIN_SIZE,
1, /* header-only LSAs */
0
);
break;
case OSPF_MSG_LS_REQ:
/* RFC2328 A.3.4, packet header followed by N>=0 12-bytes request blocks. */
ret = (bytesonwire - OSPF_HEADER_SIZE - OSPF_LS_REQ_MIN_SIZE) %
OSPF_LSA_KEY_SIZE ? MSG_NG : MSG_OK;
break;
case OSPF_MSG_LS_UPD:
/* RFC2328 A.3.5, packet header + OSPF_LS_UPD_MIN_SIZE bytes followed
by N>=0 full LSAs (with N declared beforehand). */
lsupd = (struct ospf_ls_update *) ((caddr_t) oh + OSPF_HEADER_SIZE);
ret = ospf_lsaseq_examin
(
(struct lsa_header *) ((caddr_t) lsupd + OSPF_LS_UPD_MIN_SIZE),
bytesonwire - OSPF_HEADER_SIZE - OSPF_LS_UPD_MIN_SIZE,
0, /* full LSAs */
ntohl (lsupd->num_lsas) /* 32 bits */
);
break;
case OSPF_MSG_LS_ACK:
/* RFC2328 A.3.6, packet header followed by N>=0 header-only LSAs. */
ret = ospf_lsaseq_examin
(
(struct lsa_header *) ((caddr_t) oh + OSPF_HEADER_SIZE + OSPF_LS_ACK_MIN_SIZE),
bytesonwire - OSPF_HEADER_SIZE - OSPF_LS_ACK_MIN_SIZE,
1, /* header-only LSAs */
0
);
break;
default:
if (IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: invalid packet type 0x%02x", __func__, oh->type);
return MSG_NG;
}
if (ret != MSG_OK && IS_DEBUG_OSPF_PACKET (0, RECV))
zlog_debug ("%s: malformed %s packet", __func__, LOOKUP (ospf_packet_type_str, oh->type));
return ret;
}
/* OSPF Header verification. */

View File

@ -121,6 +121,10 @@ struct ospf_db_desc
u_int32_t dd_seqnum;
};
struct ospf_ls_update
{
u_int32_t num_lsas;
};
/* Macros. */
/* XXX Perhaps obsolete; function in ospf_packet.c */