Commit 30d09eb9 authored by Ondřej Zajíček's avatar Ondřej Zajíček

OSPF: Fixes validation of LSA checksums

Prior to this patch, BIRD validates the OSPF LSA checksum by calculating
a new checksum and comparing it with the checksum in the header. Due to
the specifics of the Fletcher checksum used in OSPF, this is not
necessarily correct as the checkbytes in the header may be calculated via
a different means and end up with a different value that is nonetheless
still correct.

The documented means of validating the checksum as specified in RFC 905
B.4 is to calculate c0 and c1 from the unchanged contents of the packet,
which must result in a zero value to be considered valid.

Thanks to Chris Boot for the patch.
parent 7069fc9e
...@@ -205,7 +205,7 @@ lsasum_calculate(struct ospf_lsa_header *h, void *body) ...@@ -205,7 +205,7 @@ lsasum_calculate(struct ospf_lsa_header *h, void *body)
buf_dump("CALC", buf, length); buf_dump("CALC", buf, length);
*/ */
(void) lsasum_check(h, body); (void) lsasum_check(h, body, 1);
// log(L_WARN "Checksum result %4x", h->checksum); // log(L_WARN "Checksum result %4x", h->checksum);
...@@ -214,11 +214,21 @@ lsasum_calculate(struct ospf_lsa_header *h, void *body) ...@@ -214,11 +214,21 @@ lsasum_calculate(struct ospf_lsa_header *h, void *body)
} }
/* /*
* Note, that this function expects that LSA is in big endianity * Calculates the Fletcher checksum of an OSPF LSA.
* It also returns value in big endian *
* If 'update' is non-zero, the checkbytes (X and Y in RFC905) are calculated
* and the checksum field in the header is updated. The return value is the
* checksum as placed in the header (in network byte order).
*
* If 'update' is zero, only C0 and C1 are calculated and the header is kept
* intact. The return value is a combination of C0 and C1; if the return value
* is exactly zero the checksum is considered valid, any non-zero value is
* invalid.
*
* Note that this function expects the input LSA to be in network byte order.
*/ */
u16 u16
lsasum_check(struct ospf_lsa_header *h, void *body) lsasum_check(struct ospf_lsa_header *h, void *body, int update)
{ {
u8 *sp, *ep, *p, *q, *b; u8 *sp, *ep, *p, *q, *b;
int c0 = 0, c1 = 0; int c0 = 0, c1 = 0;
...@@ -229,7 +239,7 @@ lsasum_check(struct ospf_lsa_header *h, void *body) ...@@ -229,7 +239,7 @@ lsasum_check(struct ospf_lsa_header *h, void *body)
sp = (char *) h; sp = (char *) h;
sp += 2; /* Skip Age field */ sp += 2; /* Skip Age field */
length = ntohs(h->length) - 2; length = ntohs(h->length) - 2;
h->checksum = 0; if (update) h->checksum = 0;
for (ep = sp + length; sp < ep; sp = q) for (ep = sp + length; sp < ep; sp = q)
{ /* Actually MODX is very large, do we need the for-cyclus? */ { /* Actually MODX is very large, do we need the for-cyclus? */
...@@ -259,6 +269,14 @@ lsasum_check(struct ospf_lsa_header *h, void *body) ...@@ -259,6 +269,14 @@ lsasum_check(struct ospf_lsa_header *h, void *body)
c1 %= 255; c1 %= 255;
} }
if (!update) {
/*
* When testing the checksum, we don't need to calculate x and y. The
* checksum passes if c0 and c1 are both 0.
*/
return (c0 << 8) | (c1 & 0xff);
}
x = (int)((length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255; x = (int)((length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255;
if (x <= 0) if (x <= 0)
x += 255; x += 255;
......
...@@ -48,7 +48,7 @@ static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p) ...@@ -48,7 +48,7 @@ static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p)
int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa); int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa);
void lsasum_calculate(struct ospf_lsa_header *header, void *body); void lsasum_calculate(struct ospf_lsa_header *header, void *body);
u16 lsasum_check(struct ospf_lsa_header *h, void *body); u16 lsasum_check(struct ospf_lsa_header *h, void *body, int update);
#define CMP_NEWER 1 #define CMP_NEWER 1
#define CMP_SAME 0 #define CMP_SAME 0
#define CMP_OLDER -1 #define CMP_OLDER -1
......
...@@ -531,7 +531,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, ...@@ -531,7 +531,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
lsa_type, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum); lsa_type, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum);
/* RFC 2328 13. (1) - validate LSA checksum */ /* RFC 2328 13. (1) - validate LSA checksum */
if (lsa_n->checksum != lsasum_check(lsa_n, NULL)) if ((lsa_n->checksum == 0) || (lsasum_check(lsa_n, NULL, 0) != 0))
SKIP("invalid checksum"); SKIP("invalid checksum");
/* RFC 2328 13. (2) */ /* RFC 2328 13. (2) */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment