frr/ospf6d/ospf6_lsa.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1135 lines
31 KiB
C
Raw Normal View History

2002-12-13 21:15:29 +01:00
/*
* Copyright (C) 2003 Yasuhiro Ohara
2002-12-13 21:15:29 +01:00
*
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* GNU Zebra is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2002-12-13 21:15:29 +01:00
*/
#include <zebra.h>
/* Include other stuffs */
#include "log.h"
#include "linklist.h"
#include "vector.h"
#include "vty.h"
2002-12-13 21:15:29 +01:00
#include "command.h"
#include "memory.h"
#include "thread.h"
lib: improve fletcher checksum validation OVERVIEW The checksum used in OSPF (rfc2328) is specified in rc905 annex B. There is an sample implementation in rfc1008 which forms the basis of the quagga implementation. This algorithm works perfectly when generating a checksum; however, validation is a bit problematic. The following LSA (generated by a non-quagga implementation) is received by quagga and marked with an invalid checksum; however, it passes both the rfc905 and rfc1008 validation checks. static uint8_t lsa_10_121_233_29[] = { 0x0e, 0x10, 0x02, 0x03, 0x09, 0x00, 0x35, 0x40, 0x0a, 0x79, 0xe9, 0x1d, 0x80, 0x00, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x1c, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x36, 0xb0 }; LS Type: Summary-LSA (IP network) LS Age: 3600 seconds Do Not Age: False Options: 0x02 (E) Link-State Advertisement Type: Summary-LSA (IP network) (3) Link State ID: 9.0.53.64 Advertising Router: 10.121.233.29 (10.121.233.29) LS Sequence Number: 0x80000003 LS Checksum: 0x008a Length: 28 Netmask: 255.255.255.224 Metric: 14000 You'll note that one byte of the checksum is 0x00; quagga would calculate the checksum as 0xff8a. It can be argued that the sourcing implementation generates an incorrect checksum; however, rfc905 indicates that, for 1's complement arithmetic, the value 255 shall be regarded as 0, thus either values are valid. EXPLANATION The quagga ospfd and ospf6d implementations operate by copying the PDU's existing checksum in a holding variable, calculating the checksum, and comparing the resulting checksum to the original. As a note, this implementation has the side effect of modifying the contents of the PDU. Evaluation of both rfc905 and rfc1008 shows that checksum validation should involve calculating the sum over the PDU and checking that both resulting C0 and C1 values are zero. This behavior is enacted in the rfc1008 implementation by calling encodecc with k = 0 (checksum offset); however, this functionality had been omitted from the quagga implementation. PATCH This patch adds the ability to call the quagga's fletcher_checksum() with a checksum offset value of 0xffff (aka FLETCHER_CHECKSUM_VALIDATE) which returns the sum over the buffer (a value of 0 indicates a valid checksum). This is similar to the mechanism in rfc1008 when called with k = 0. The patch also introduces ospf_lsa_checksum_valid(). ospf6d had it's own implementation of the fletcher checksum in ospf6_lsa_checksum(); it's the same algorithm as in fletcher_checksum(). This patch removes the local implementation in favor of the library's as well as creates and uses ospf6_lsa_checksum_valid(). quagga's ISIS implementation suffers from the same problem; however, I do not have the facilities to validate a fix to ISIS, thus this change has been left to the ISIS maintainers. The function iso_csum_verify() should be reduced to running the fletcher checksum over the buffer using an offset of 0. Signed-off-by: JR Rivers <jrrivers@cumulusnetworks.com> Reviewed-by: Scott Feldman <sfeldma@cumulusnetworks.com> Reviewed-by: Nolan Leake <nolan@cumulusnetworks.com> Reviewed-by: Ayan Banerjee <ayan@cumulusnetworks.com> Reviewed-by: Shrijeet Mukherjee <shm@cumulusnetworks.com> Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
2012-09-13 19:17:36 +02:00
#include "checksum.h"
2002-12-13 21:15:29 +01:00
#include "ospf6_proto.h"
#include "ospf6_lsa.h"
#include "ospf6_lsdb.h"
#include "ospf6_message.h"
#include "ospf6_asbr.h"
#include "ospf6_zebra.h"
2002-12-13 21:15:29 +01:00
#include "ospf6_top.h"
#include "ospf6_area.h"
#include "ospf6_interface.h"
#include "ospf6_neighbor.h"
#include "ospf6_flood.h"
#include "ospf6d.h"
2002-12-13 21:15:29 +01:00
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_HEADER, "OSPF6 LSA header");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_SUMMARY, "OSPF6 LSA summary");
vector ospf6_lsa_handler_vector;
2002-12-13 21:15:29 +01:00
struct ospf6 *ospf6_get_by_lsdb(struct ospf6_lsa *lsa)
{
struct ospf6 *ospf6 = NULL;
switch (OSPF6_LSA_SCOPE(lsa->header->type)) {
case OSPF6_SCOPE_LINKLOCAL:
ospf6 = OSPF6_INTERFACE(lsa->lsdb->data)->area->ospf6;
break;
case OSPF6_SCOPE_AREA:
ospf6 = OSPF6_AREA(lsa->lsdb->data)->ospf6;
break;
case OSPF6_SCOPE_AS:
ospf6 = OSPF6_PROCESS(lsa->lsdb->data);
break;
default:
assert(0);
break;
}
return ospf6;
}
static int ospf6_unknown_lsa_show(struct vty *vty, struct ospf6_lsa *lsa,
json_object *json_obj, bool use_json)
{
uint8_t *start, *end, *current;
char byte[4];
start = (uint8_t *)lsa->header + sizeof(struct ospf6_lsa_header);
end = (uint8_t *)lsa->header + ntohs(lsa->header->length);
if (use_json)
json_object_string_add(json_obj, "LsaType", "unknown");
else {
vty_out(vty, " Unknown contents:\n");
for (current = start; current < end; current++) {
if ((current - start) % 16 == 0)
vty_out(vty, "\n ");
else if ((current - start) % 4 == 0)
vty_out(vty, " ");
snprintf(byte, sizeof(byte), "%02x", *current);
vty_out(vty, "%s", byte);
}
2002-12-13 21:15:29 +01:00
vty_out(vty, "\n\n");
}
return 0;
}
static struct ospf6_lsa_handler unknown_handler = {
.lh_type = OSPF6_LSTYPE_UNKNOWN,
.lh_name = "Unknown",
.lh_short_name = "Unk",
.lh_show = ospf6_unknown_lsa_show,
.lh_get_prefix_str = NULL,
.lh_debug = 0 /* No default debug */
};
void ospf6_install_lsa_handler(struct ospf6_lsa_handler *handler)
{
/* type in handler is host byte order */
int index = handler->lh_type & OSPF6_LSTYPE_FCODE_MASK;
vector_set_index(ospf6_lsa_handler_vector, index, (void *)handler);
}
struct ospf6_lsa_handler *ospf6_get_lsa_handler(uint16_t type)
{
struct ospf6_lsa_handler *handler = NULL;
unsigned int index = ntohs(type) & OSPF6_LSTYPE_FCODE_MASK;
if (index >= vector_active(ospf6_lsa_handler_vector))
handler = &unknown_handler;
else
handler = vector_slot(ospf6_lsa_handler_vector, index);
if (handler == NULL)
handler = &unknown_handler;
return handler;
}
2002-12-13 21:15:29 +01:00
const char *ospf6_lstype_name(uint16_t type)
{
static char buf[8];
const struct ospf6_lsa_handler *handler;
handler = ospf6_get_lsa_handler(type);
if (handler && handler != &unknown_handler)
return handler->lh_name;
2002-12-13 21:15:29 +01:00
snprintf(buf, sizeof(buf), "0x%04hx", ntohs(type));
return buf;
2002-12-13 21:15:29 +01:00
}
const char *ospf6_lstype_short_name(uint16_t type)
{
static char buf[8];
const struct ospf6_lsa_handler *handler;
handler = ospf6_get_lsa_handler(type);
if (handler)
return handler->lh_short_name;
snprintf(buf, sizeof(buf), "0x%04hx", ntohs(type));
return buf;
}
uint8_t ospf6_lstype_debug(uint16_t type)
{
const struct ospf6_lsa_handler *handler;
handler = ospf6_get_lsa_handler(type);
return handler->lh_debug;
}
int metric_type(struct ospf6 *ospf6, int type, uint8_t instance)
{
struct ospf6_redist *red;
red = ospf6_redist_lookup(ospf6, type, instance);
return ((!red || red->dmetric.type < 0) ? DEFAULT_METRIC_TYPE
: red->dmetric.type);
}
int metric_value(struct ospf6 *ospf6, int type, uint8_t instance)
{
struct ospf6_redist *red;
red = ospf6_redist_lookup(ospf6, type, instance);
if (!red || red->dmetric.value < 0) {
if (type == DEFAULT_ROUTE) {
if (ospf6->default_originate == DEFAULT_ORIGINATE_ZEBRA)
return DEFAULT_DEFAULT_ORIGINATE_METRIC;
else
return DEFAULT_DEFAULT_ALWAYS_METRIC;
} else
return DEFAULT_DEFAULT_METRIC;
}
return red->dmetric.value;
}
2002-12-13 21:15:29 +01:00
/* RFC2328: Section 13.2 */
int ospf6_lsa_is_differ(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2)
2002-12-13 21:15:29 +01:00
{
int len;
2002-12-13 21:15:29 +01:00
assert(OSPF6_LSA_IS_SAME(lsa1, lsa2));
2002-12-13 21:15:29 +01:00
/* XXX, Options ??? */
2002-12-13 21:15:29 +01:00
ospf6_lsa_age_current(lsa1);
ospf6_lsa_age_current(lsa2);
if (ntohs(lsa1->header->age) == OSPF_LSA_MAXAGE
&& ntohs(lsa2->header->age) != OSPF_LSA_MAXAGE)
2002-12-13 21:15:29 +01:00
return 1;
if (ntohs(lsa1->header->age) != OSPF_LSA_MAXAGE
&& ntohs(lsa2->header->age) == OSPF_LSA_MAXAGE)
2002-12-13 21:15:29 +01:00
return 1;
/* compare body */
if (ntohs(lsa1->header->length) != ntohs(lsa2->header->length))
return 1;
len = ntohs(lsa1->header->length) - sizeof(struct ospf6_lsa_header);
return memcmp(lsa1->header + 1, lsa2->header + 1, len);
2002-12-13 21:15:29 +01:00
}
int ospf6_lsa_is_changed(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2)
2002-12-13 21:15:29 +01:00
{
int length;
2002-12-13 21:15:29 +01:00
if (OSPF6_LSA_IS_MAXAGE(lsa1) ^ OSPF6_LSA_IS_MAXAGE(lsa2))
return 1;
if (ntohs(lsa1->header->length) != ntohs(lsa2->header->length))
return 1;
/* Going beyond LSA headers to compare the payload only makes sense,
* when both LSAs aren't header-only. */
if (CHECK_FLAG(lsa1->flag, OSPF6_LSA_HEADERONLY)
!= CHECK_FLAG(lsa2->flag, OSPF6_LSA_HEADERONLY)) {
zlog_warn(
"%s: only one of two (%s, %s) LSAs compared is header-only",
__func__, lsa1->name, lsa2->name);
return 1;
}
if (CHECK_FLAG(lsa1->flag, OSPF6_LSA_HEADERONLY))
return 0;
2002-12-13 21:15:29 +01:00
length = OSPF6_LSA_SIZE(lsa1->header) - sizeof(struct ospf6_lsa_header);
/* Once upper layer verifies LSAs received, length underrun should
* become a warning. */
if (length <= 0)
return 0;
2002-12-13 21:15:29 +01:00
return memcmp(OSPF6_LSA_HEADER_END(lsa1->header),
OSPF6_LSA_HEADER_END(lsa2->header), length);
2002-12-13 21:15:29 +01:00
}
/* ospf6 age functions */
/* calculate birth */
void ospf6_lsa_age_set(struct ospf6_lsa *lsa)
2002-12-13 21:15:29 +01:00
{
struct timeval now;
assert(lsa && lsa->header);
monotime(&now);
2002-12-13 21:15:29 +01:00
lsa->birth.tv_sec = now.tv_sec - ntohs(lsa->header->age);
lsa->birth.tv_usec = now.tv_usec;
2002-12-13 21:15:29 +01:00
return;
}
/* this function calculates current age from its birth,
then update age field of LSA header. return value is current age */
uint16_t ospf6_lsa_age_current(struct ospf6_lsa *lsa)
2002-12-13 21:15:29 +01:00
{
struct timeval now;
uint32_t ulage;
uint16_t age;
2002-12-13 21:15:29 +01:00
assert(lsa);
assert(lsa->header);
/* current time */
monotime(&now);
2002-12-13 21:15:29 +01:00
if (ntohs(lsa->header->age) >= OSPF_LSA_MAXAGE) {
/* ospf6_lsa_premature_aging () sets age to MAXAGE; when using
relative time, we cannot compare against lsa birth time, so
we catch this special case here. */
lsa->header->age = htons(OSPF_LSA_MAXAGE);
return OSPF_LSA_MAXAGE;
}
2002-12-13 21:15:29 +01:00
/* calculate age */
ulage = now.tv_sec - lsa->birth.tv_sec;
/* if over MAXAGE, set to it */
age = (ulage > OSPF_LSA_MAXAGE ? OSPF_LSA_MAXAGE : ulage);
2002-12-13 21:15:29 +01:00
lsa->header->age = htons(age);
return age;
}
/* update age field of LSA header with adding InfTransDelay */
void ospf6_lsa_age_update_to_send(struct ospf6_lsa *lsa, uint32_t transdelay)
2002-12-13 21:15:29 +01:00
{
unsigned short age;
2002-12-13 21:15:29 +01:00
age = ospf6_lsa_age_current(lsa) + transdelay;
if (age > OSPF_LSA_MAXAGE)
age = OSPF_LSA_MAXAGE;
2002-12-13 21:15:29 +01:00
lsa->header->age = htons(age);
}
2002-12-13 21:15:29 +01:00
void ospf6_lsa_premature_aging(struct ospf6_lsa *lsa)
{
/* log */
if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type))
zlog_debug("LSA: Premature aging: %s", lsa->name);
THREAD_OFF(lsa->expire);
THREAD_OFF(lsa->refresh);
/*
* We clear the LSA from the neighbor retx lists now because it
* will not get deleted later. Essentially, changing the age to
* MaxAge will prevent this LSA from being matched with its
* existing entries in the retx list thereby causing those entries
* to be silently replaced with its MaxAged version, but with ever
* increasing retx count causing this LSA to remain forever and
* for the MaxAge remover thread to be called forever too.
*
* The reason the previous entry silently disappears is that when
* entry is added to a neighbor's retx list, it replaces the existing
* entry. But since the ospf6_lsdb_add() routine is generic and not
* aware
* of the special semantics of retx count, the retx count is not
* decremented when its replaced. Attempting to add the incr and decr
* retx count routines as the hook_add and hook_remove for the retx
* lists
* have a problem because the hook_remove routine is called for MaxAge
* entries (as will be the case in a traditional LSDB, unlike in this
* case
* where an LSDB is used as an efficient tree structure to store all
* kinds
* of data) that are added instead of calling the hook_add routine.
*/
ospf6_flood_clear(lsa);
lsa->header->age = htons(OSPF_LSA_MAXAGE);
2002-12-13 21:15:29 +01:00
thread_execute(master, ospf6_lsa_expire, lsa, 0);
}
/* check which is more recent. if a is more recent, return -1;
if the same, return 0; otherwise(b is more recent), return 1 */
int ospf6_lsa_compare(struct ospf6_lsa *a, struct ospf6_lsa *b)
2002-12-13 21:15:29 +01:00
{
int32_t seqnuma, seqnumb;
uint16_t cksuma, cksumb;
uint16_t agea, ageb;
2002-12-13 21:15:29 +01:00
assert(a && a->header);
assert(b && b->header);
assert(OSPF6_LSA_IS_SAME(a, b));
2002-12-13 21:15:29 +01:00
seqnuma = (int32_t)ntohl(a->header->seqnum);
seqnumb = (int32_t)ntohl(b->header->seqnum);
/* compare by sequence number */
if (seqnuma > seqnumb)
return -1;
if (seqnuma < seqnumb)
2002-12-13 21:15:29 +01:00
return 1;
2002-12-13 21:15:29 +01:00
/* Checksum */
cksuma = ntohs(a->header->checksum);
cksumb = ntohs(b->header->checksum);
if (cksuma > cksumb)
return -1;
if (cksuma < cksumb)
return 0;
/* Update Age */
2002-12-13 21:15:29 +01:00
agea = ospf6_lsa_age_current(a);
ageb = ospf6_lsa_age_current(b);
/* MaxAge check */
if (agea == OSPF_LSA_MAXAGE && ageb != OSPF_LSA_MAXAGE)
2002-12-13 21:15:29 +01:00
return -1;
else if (agea != OSPF_LSA_MAXAGE && ageb == OSPF_LSA_MAXAGE)
2002-12-13 21:15:29 +01:00
return 1;
/* Age check */
if (agea > ageb && agea - ageb >= OSPF_LSA_MAXAGE_DIFF)
2002-12-13 21:15:29 +01:00
return 1;
else if (agea < ageb && ageb - agea >= OSPF_LSA_MAXAGE_DIFF)
2002-12-13 21:15:29 +01:00
return -1;
2002-12-13 21:15:29 +01:00
/* neither recent */
return 0;
}
char *ospf6_lsa_printbuf(struct ospf6_lsa *lsa, char *buf, int size)
2002-12-13 21:15:29 +01:00
{
char id[16], adv_router[16];
inet_ntop(AF_INET, &lsa->header->id, id, sizeof(id));
inet_ntop(AF_INET, &lsa->header->adv_router, adv_router,
sizeof(adv_router));
snprintf(buf, size, "[%s Id:%s Adv:%s]",
ospf6_lstype_name(lsa->header->type), id, adv_router);
return buf;
2002-12-13 21:15:29 +01:00
}
void ospf6_lsa_header_print_raw(struct ospf6_lsa_header *header)
2002-12-13 21:15:29 +01:00
{
char id[16], adv_router[16];
inet_ntop(AF_INET, &header->id, id, sizeof(id));
inet_ntop(AF_INET, &header->adv_router, adv_router, sizeof(adv_router));
zlog_debug(" [%s Id:%s Adv:%s]", ospf6_lstype_name(header->type), id,
adv_router);
zlog_debug(" Age: %4hu SeqNum: %#08lx Cksum: %04hx Len: %d",
ntohs(header->age), (unsigned long)ntohl(header->seqnum),
ntohs(header->checksum), ntohs(header->length));
2002-12-13 21:15:29 +01:00
}
void ospf6_lsa_header_print(struct ospf6_lsa *lsa)
2002-12-13 21:15:29 +01:00
{
ospf6_lsa_age_current(lsa);
ospf6_lsa_header_print_raw(lsa->header);
2002-12-13 21:15:29 +01:00
}
2002-12-13 21:15:29 +01:00
void ospf6_lsa_show_summary_header(struct vty *vty)
{
vty_out(vty, "%-4s %-15s%-15s%4s %8s %30s\n", "Type", "LSId",
2002-12-13 21:15:29 +01:00
"AdvRouter", "Age", "SeqNum", "Payload");
}
void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa,
json_object *json_array, bool use_json)
2002-12-13 21:15:29 +01:00
{
char adv_router[16], id[16];
int type;
const struct ospf6_lsa_handler *handler;
char buf[64];
int cnt = 0;
json_object *json_obj = NULL;
2002-12-13 21:15:29 +01:00
assert(lsa);
assert(lsa->header);
2002-12-13 21:15:29 +01:00
inet_ntop(AF_INET, &lsa->header->id, id, sizeof(id));
inet_ntop(AF_INET, &lsa->header->adv_router, adv_router,
sizeof(adv_router));
type = ntohs(lsa->header->type);
handler = ospf6_get_lsa_handler(lsa->header->type);
if (use_json)
json_obj = json_object_new_object();
switch (type) {
case OSPF6_LSTYPE_INTER_PREFIX:
case OSPF6_LSTYPE_INTER_ROUTER:
case OSPF6_LSTYPE_AS_EXTERNAL:
case OSPF6_LSTYPE_TYPE_7:
if (use_json) {
json_object_string_add(
json_obj, "type",
ospf6_lstype_short_name(lsa->header->type));
json_object_string_add(json_obj, "lsId", id);
json_object_string_add(json_obj, "advRouter",
adv_router);
json_object_int_add(json_obj, "age",
ospf6_lsa_age_current(lsa));
json_object_int_add(
json_obj, "seqNum",
(unsigned long)ntohl(lsa->header->seqnum));
json_object_string_add(
json_obj, "payload",
handler->lh_get_prefix_str(lsa, buf,
sizeof(buf), 0));
json_object_array_add(json_array, json_obj);
} else
vty_out(vty, "%-4s %-15s%-15s%4hu %8lx %30s\n",
ospf6_lstype_short_name(lsa->header->type), id,
adv_router, ospf6_lsa_age_current(lsa),
(unsigned long)ntohl(lsa->header->seqnum),
handler->lh_get_prefix_str(lsa, buf,
sizeof(buf), 0));
break;
case OSPF6_LSTYPE_ROUTER:
case OSPF6_LSTYPE_NETWORK:
case OSPF6_LSTYPE_GROUP_MEMBERSHIP:
case OSPF6_LSTYPE_LINK:
case OSPF6_LSTYPE_INTRA_PREFIX:
while (handler->lh_get_prefix_str(lsa, buf, sizeof(buf), cnt)
!= NULL) {
if (use_json) {
json_object_string_add(
json_obj, "type",
ospf6_lstype_short_name(
lsa->header->type));
json_object_string_add(json_obj, "lsId", id);
json_object_string_add(json_obj, "advRouter",
adv_router);
json_object_int_add(json_obj, "age",
ospf6_lsa_age_current(lsa));
json_object_int_add(
json_obj, "seqNum",
(unsigned long)ntohl(
lsa->header->seqnum));
json_object_string_add(json_obj, "payload",
buf);
json_object_array_add(json_array, json_obj);
json_obj = json_object_new_object();
} else
vty_out(vty, "%-4s %-15s%-15s%4hu %8lx %30s\n",
ospf6_lstype_short_name(
lsa->header->type),
id, adv_router,
ospf6_lsa_age_current(lsa),
(unsigned long)ntohl(
lsa->header->seqnum),
buf);
cnt++;
}
if (use_json)
json_object_free(json_obj);
break;
default:
if (use_json) {
json_object_string_add(
json_obj, "type",
ospf6_lstype_short_name(lsa->header->type));
json_object_string_add(json_obj, "lsId", id);
json_object_string_add(json_obj, "advRouter",
adv_router);
json_object_int_add(json_obj, "age",
ospf6_lsa_age_current(lsa));
json_object_int_add(
json_obj, "seqNum",
(unsigned long)ntohl(lsa->header->seqnum));
json_object_array_add(json_array, json_obj);
} else
vty_out(vty, "%-4s %-15s%-15s%4hu %8lx\n",
ospf6_lstype_short_name(lsa->header->type), id,
adv_router, ospf6_lsa_age_current(lsa),
(unsigned long)ntohl(lsa->header->seqnum));
break;
}
}
void ospf6_lsa_show_dump(struct vty *vty, struct ospf6_lsa *lsa,
json_object *json_array, bool use_json)
2002-12-13 21:15:29 +01:00
{
uint8_t *start, *end, *current;
2002-12-13 21:15:29 +01:00
char byte[4];
start = (uint8_t *)lsa->header;
end = (uint8_t *)lsa->header + ntohs(lsa->header->length);
if (use_json)
return;
vty_out(vty, "\n");
vty_out(vty, "%s:\n", lsa->name);
2002-12-13 21:15:29 +01:00
for (current = start; current < end; current++) {
if ((current - start) % 16 == 0)
vty_out(vty, "\n ");
2002-12-13 21:15:29 +01:00
else if ((current - start) % 4 == 0)
vty_out(vty, " ");
2002-12-13 21:15:29 +01:00
snprintf(byte, sizeof(byte), "%02x", *current);
vty_out(vty, "%s", byte);
}
vty_out(vty, "\n\n");
return;
2002-12-13 21:15:29 +01:00
}
void ospf6_lsa_show_internal(struct vty *vty, struct ospf6_lsa *lsa,
json_object *json_array, bool use_json)
{
char adv_router[64], id[64];
json_object *json_obj;
assert(lsa && lsa->header);
inet_ntop(AF_INET, &lsa->header->id, id, sizeof(id));
inet_ntop(AF_INET, &lsa->header->adv_router, adv_router,
sizeof(adv_router));
if (use_json) {
json_obj = json_object_new_object();
json_object_int_add(json_obj, "age",
ospf6_lsa_age_current(lsa));
json_object_string_add(json_obj, "type",
ospf6_lstype_name(lsa->header->type));
json_object_string_add(json_obj, "linkStateId", id);
json_object_string_add(json_obj, "advertisingRouter",
adv_router);
json_object_int_add(json_obj, "lsSequenceNumber",
(unsigned long)ntohl(lsa->header->seqnum));
json_object_int_add(json_obj, "checksum",
ntohs(lsa->header->checksum));
json_object_int_add(json_obj, "length",
ntohs(lsa->header->length));
json_object_int_add(json_obj, "flag", lsa->flag);
json_object_int_add(json_obj, "lock", lsa->lock);
json_object_int_add(json_obj, "reTxCount", lsa->retrans_count);
/* Threads Data not added */
json_object_array_add(json_array, json_obj);
} else {
vty_out(vty, "\n");
vty_out(vty, "Age: %4hu Type: %s\n", ospf6_lsa_age_current(lsa),
ospf6_lstype_name(lsa->header->type));
vty_out(vty, "Link State ID: %s\n", id);
vty_out(vty, "Advertising Router: %s\n", adv_router);
vty_out(vty, "LS Sequence Number: %#010lx\n",
(unsigned long)ntohl(lsa->header->seqnum));
vty_out(vty, "CheckSum: %#06hx Length: %hu\n",
ntohs(lsa->header->checksum),
ntohs(lsa->header->length));
vty_out(vty, "Flag: %x \n", lsa->flag);
vty_out(vty, "Lock: %d \n", lsa->lock);
vty_out(vty, "ReTx Count: %d\n", lsa->retrans_count);
vty_out(vty, "Threads: Expire: %p, Refresh: %p\n", lsa->expire,
lsa->refresh);
vty_out(vty, "\n");
}
return;
}
void ospf6_lsa_show(struct vty *vty, struct ospf6_lsa *lsa,
json_object *json_array, bool use_json)
{
char adv_router[64], id[64];
const struct ospf6_lsa_handler *handler;
struct timeval now, res;
char duration[64];
json_object *json_obj = NULL;
assert(lsa && lsa->header);
inet_ntop(AF_INET, &lsa->header->id, id, sizeof(id));
inet_ntop(AF_INET, &lsa->header->adv_router, adv_router,
sizeof(adv_router));
monotime(&now);
timersub(&now, &lsa->installed, &res);
timerstring(&res, duration, sizeof(duration));
if (use_json) {
json_obj = json_object_new_object();
json_object_int_add(json_obj, "age",
ospf6_lsa_age_current(lsa));
json_object_string_add(json_obj, "type",
ospf6_lstype_name(lsa->header->type));
json_object_string_add(json_obj, "linkStateId", id);
json_object_string_add(json_obj, "advertisingRouter",
adv_router);
json_object_int_add(json_obj, "lsSequenceNumber",
(unsigned long)ntohl(lsa->header->seqnum));
json_object_int_add(json_obj, "checkSum",
ntohs(lsa->header->checksum));
json_object_int_add(json_obj, "length",
ntohs(lsa->header->length));
json_object_string_add(json_obj, "duration", duration);
} else {
vty_out(vty, "Age: %4hu Type: %s\n", ospf6_lsa_age_current(lsa),
ospf6_lstype_name(lsa->header->type));
vty_out(vty, "Link State ID: %s\n", id);
vty_out(vty, "Advertising Router: %s\n", adv_router);
vty_out(vty, "LS Sequence Number: %#010lx\n",
(unsigned long)ntohl(lsa->header->seqnum));
vty_out(vty, "CheckSum: %#06hx Length: %hu\n",
ntohs(lsa->header->checksum),
ntohs(lsa->header->length));
vty_out(vty, "Duration: %s\n", duration);
}
handler = ospf6_get_lsa_handler(lsa->header->type);
if (handler->lh_show != NULL)
handler->lh_show(vty, lsa, json_obj, use_json);
else {
assert(unknown_handler.lh_show != NULL);
unknown_handler.lh_show(vty, lsa, json_obj, use_json);
}
if (use_json)
json_object_array_add(json_array, json_obj);
else
vty_out(vty, "\n");
}
struct ospf6_lsa *ospf6_lsa_alloc(size_t lsa_length)
{
struct ospf6_lsa *lsa;
lsa = XCALLOC(MTYPE_OSPF6_LSA, sizeof(struct ospf6_lsa));
lsa->header = XMALLOC(MTYPE_OSPF6_LSA_HEADER, lsa_length);
return lsa;
}
/* OSPFv3 LSA creation/deletion function */
struct ospf6_lsa *ospf6_lsa_create(struct ospf6_lsa_header *header)
2002-12-13 21:15:29 +01:00
{
struct ospf6_lsa *lsa = NULL;
uint16_t lsa_size = 0;
2002-12-13 21:15:29 +01:00
/* size of the entire LSA */
lsa_size = ntohs(header->length); /* XXX vulnerable */
2002-12-13 21:15:29 +01:00
lsa = ospf6_lsa_alloc(lsa_size);
2002-12-13 21:15:29 +01:00
/* copy LSA from original header */
memcpy(lsa->header, header, lsa_size);
2002-12-13 21:15:29 +01:00
/* dump string */
ospf6_lsa_printbuf(lsa, lsa->name, sizeof(lsa->name));
2002-12-13 21:15:29 +01:00
/* calculate birth of this lsa */
2002-12-13 21:15:29 +01:00
ospf6_lsa_age_set(lsa);
return lsa;
}
struct ospf6_lsa *ospf6_lsa_create_headeronly(struct ospf6_lsa_header *header)
2002-12-13 21:15:29 +01:00
{
struct ospf6_lsa *lsa = NULL;
lsa = ospf6_lsa_alloc(sizeof(struct ospf6_lsa_header));
2002-12-13 21:15:29 +01:00
memcpy(lsa->header, header, sizeof(struct ospf6_lsa_header));
2002-12-13 21:15:29 +01:00
SET_FLAG(lsa->flag, OSPF6_LSA_HEADERONLY);
2002-12-13 21:15:29 +01:00
/* dump string */
ospf6_lsa_printbuf(lsa, lsa->name, sizeof(lsa->name));
2002-12-13 21:15:29 +01:00
/* calculate birth of this lsa */
2002-12-13 21:15:29 +01:00
ospf6_lsa_age_set(lsa);
return lsa;
}
void ospf6_lsa_delete(struct ospf6_lsa *lsa)
{
assert(lsa->lock == 0);
2002-12-13 21:15:29 +01:00
/* cancel threads */
THREAD_OFF(lsa->expire);
THREAD_OFF(lsa->refresh);
2002-12-13 21:15:29 +01:00
/* do free */
XFREE(MTYPE_OSPF6_LSA_HEADER, lsa->header);
XFREE(MTYPE_OSPF6_LSA, lsa);
}
2002-12-13 21:15:29 +01:00
struct ospf6_lsa *ospf6_lsa_copy(struct ospf6_lsa *lsa)
{
struct ospf6_lsa *copy = NULL;
ospf6_lsa_age_current(lsa);
if (CHECK_FLAG(lsa->flag, OSPF6_LSA_HEADERONLY))
copy = ospf6_lsa_create_headeronly(lsa->header);
2002-12-13 21:15:29 +01:00
else
copy = ospf6_lsa_create(lsa->header);
assert(copy->lock == 0);
copy->birth = lsa->birth;
copy->originated = lsa->originated;
copy->received = lsa->received;
copy->installed = lsa->installed;
copy->lsdb = lsa->lsdb;
copy->rn = NULL;
return copy;
2002-12-13 21:15:29 +01:00
}
/* increment reference counter of struct ospf6_lsa */
struct ospf6_lsa *ospf6_lsa_lock(struct ospf6_lsa *lsa)
2002-12-13 21:15:29 +01:00
{
lsa->lock++;
return lsa;
2002-12-13 21:15:29 +01:00
}
/* decrement reference counter of struct ospf6_lsa */
struct ospf6_lsa *ospf6_lsa_unlock(struct ospf6_lsa *lsa)
2002-12-13 21:15:29 +01:00
{
/* decrement reference counter */
assert(lsa->lock > 0);
lsa->lock--;
2002-12-13 21:15:29 +01:00
if (lsa->lock != 0)
return lsa;
ospf6_lsa_delete(lsa);
return NULL;
2002-12-13 21:15:29 +01:00
}
/* ospf6 lsa expiry */
2002-12-13 21:15:29 +01:00
int ospf6_lsa_expire(struct thread *thread)
{
struct ospf6_lsa *lsa;
struct ospf6 *ospf6;
2002-12-13 21:15:29 +01:00
lsa = (struct ospf6_lsa *)THREAD_ARG(thread);
assert(lsa && lsa->header);
assert(OSPF6_LSA_IS_MAXAGE(lsa));
assert(!lsa->refresh);
2002-12-13 21:15:29 +01:00
lsa->expire = (struct thread *)NULL;
if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)) {
zlog_debug("LSA Expire:");
ospf6_lsa_header_print(lsa);
}
2002-12-13 21:15:29 +01:00
if (CHECK_FLAG(lsa->flag, OSPF6_LSA_HEADERONLY))
return 0; /* dbexchange will do something ... */
ospf6 = ospf6_get_by_lsdb(lsa);
/* reinstall lsa */
ospf6_install_lsa(lsa);
/* reflood lsa */
ospf6_flood(NULL, lsa);
/* schedule maxage remover */
ospf6_maxage_remove(ospf6);
2002-12-13 21:15:29 +01:00
return 0;
}
int ospf6_lsa_refresh(struct thread *thread)
{
struct ospf6_lsa *old, *self, *new;
struct ospf6_lsdb *lsdb_self;
old = (struct ospf6_lsa *)THREAD_ARG(thread);
assert(old && old->header);
old->refresh = (struct thread *)NULL;
lsdb_self = ospf6_get_scoped_lsdb_self(old);
self = ospf6_lsdb_lookup(old->header->type, old->header->id,
old->header->adv_router, lsdb_self);
if (self == NULL) {
if (IS_OSPF6_DEBUG_LSA_TYPE(old->header->type))
zlog_debug("Refresh: could not find self LSA, flush %s",
old->name);
ospf6_lsa_premature_aging(old);
return 0;
}
2002-12-13 21:15:29 +01:00
/* Reset age, increment LS sequence number. */
self->header->age = htons(0);
self->header->seqnum =
ospf6_new_ls_seqnum(self->header->type, self->header->id,
self->header->adv_router, old->lsdb);
ospf6_lsa_checksum(self->header);
new = ospf6_lsa_create(self->header);
new->lsdb = old->lsdb;
new->refresh = NULL;
thread_add_timer(master, ospf6_lsa_refresh, new, OSPF_LS_REFRESH_TIME,
&new->refresh);
/* store it in the LSDB for self-originated LSAs */
ospf6_lsdb_add(ospf6_lsa_copy(new), lsdb_self);
if (IS_OSPF6_DEBUG_LSA_TYPE(new->header->type)) {
zlog_debug("LSA Refresh:");
ospf6_lsa_header_print(new);
2002-12-13 21:15:29 +01:00
}
ospf6_install_lsa(new);
ospf6_flood(NULL, new);
return 0;
2002-12-13 21:15:29 +01:00
}
void ospf6_flush_self_originated_lsas_now(struct ospf6 *ospf6)
{
struct listnode *node, *nnode;
struct ospf6_area *oa;
struct ospf6_lsa *lsa;
const struct route_node *end = NULL;
uint32_t type, adv_router;
struct ospf6_interface *oi;
ospf6->inst_shutdown = 1;
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
end = ospf6_lsdb_head(oa->lsdb_self, 0, 0, ospf6->router_id,
&lsa);
while (lsa) {
/* RFC 2328 (14.1): Set MAXAGE */
lsa->header->age = htons(OSPF_LSA_MAXAGE);
/* Flood MAXAGE LSA*/
ospf6_flood(NULL, lsa);
lsa = ospf6_lsdb_next(end, lsa);
}
for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) {
end = ospf6_lsdb_head(oi->lsdb_self, 0, 0,
ospf6->router_id, &lsa);
while (lsa) {
/* RFC 2328 (14.1): Set MAXAGE */
lsa->header->age = htons(OSPF_LSA_MAXAGE);
/* Flood MAXAGE LSA*/
ospf6_flood(NULL, lsa);
lsa = ospf6_lsdb_next(end, lsa);
}
}
}
type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
adv_router = ospf6->router_id;
for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, adv_router, lsa)) {
/* RFC 2328 (14.1): Set MAXAGE */
lsa->header->age = htons(OSPF_LSA_MAXAGE);
ospf6_flood(NULL, lsa);
}
}
lib: improve fletcher checksum validation OVERVIEW The checksum used in OSPF (rfc2328) is specified in rc905 annex B. There is an sample implementation in rfc1008 which forms the basis of the quagga implementation. This algorithm works perfectly when generating a checksum; however, validation is a bit problematic. The following LSA (generated by a non-quagga implementation) is received by quagga and marked with an invalid checksum; however, it passes both the rfc905 and rfc1008 validation checks. static uint8_t lsa_10_121_233_29[] = { 0x0e, 0x10, 0x02, 0x03, 0x09, 0x00, 0x35, 0x40, 0x0a, 0x79, 0xe9, 0x1d, 0x80, 0x00, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x1c, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x36, 0xb0 }; LS Type: Summary-LSA (IP network) LS Age: 3600 seconds Do Not Age: False Options: 0x02 (E) Link-State Advertisement Type: Summary-LSA (IP network) (3) Link State ID: 9.0.53.64 Advertising Router: 10.121.233.29 (10.121.233.29) LS Sequence Number: 0x80000003 LS Checksum: 0x008a Length: 28 Netmask: 255.255.255.224 Metric: 14000 You'll note that one byte of the checksum is 0x00; quagga would calculate the checksum as 0xff8a. It can be argued that the sourcing implementation generates an incorrect checksum; however, rfc905 indicates that, for 1's complement arithmetic, the value 255 shall be regarded as 0, thus either values are valid. EXPLANATION The quagga ospfd and ospf6d implementations operate by copying the PDU's existing checksum in a holding variable, calculating the checksum, and comparing the resulting checksum to the original. As a note, this implementation has the side effect of modifying the contents of the PDU. Evaluation of both rfc905 and rfc1008 shows that checksum validation should involve calculating the sum over the PDU and checking that both resulting C0 and C1 values are zero. This behavior is enacted in the rfc1008 implementation by calling encodecc with k = 0 (checksum offset); however, this functionality had been omitted from the quagga implementation. PATCH This patch adds the ability to call the quagga's fletcher_checksum() with a checksum offset value of 0xffff (aka FLETCHER_CHECKSUM_VALIDATE) which returns the sum over the buffer (a value of 0 indicates a valid checksum). This is similar to the mechanism in rfc1008 when called with k = 0. The patch also introduces ospf_lsa_checksum_valid(). ospf6d had it's own implementation of the fletcher checksum in ospf6_lsa_checksum(); it's the same algorithm as in fletcher_checksum(). This patch removes the local implementation in favor of the library's as well as creates and uses ospf6_lsa_checksum_valid(). quagga's ISIS implementation suffers from the same problem; however, I do not have the facilities to validate a fix to ISIS, thus this change has been left to the ISIS maintainers. The function iso_csum_verify() should be reduced to running the fletcher checksum over the buffer using an offset of 0. Signed-off-by: JR Rivers <jrrivers@cumulusnetworks.com> Reviewed-by: Scott Feldman <sfeldma@cumulusnetworks.com> Reviewed-by: Nolan Leake <nolan@cumulusnetworks.com> Reviewed-by: Ayan Banerjee <ayan@cumulusnetworks.com> Reviewed-by: Shrijeet Mukherjee <shm@cumulusnetworks.com> Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
2012-09-13 19:17:36 +02:00
/* Fletcher Checksum -- Refer to RFC1008. */
2002-12-13 21:15:29 +01:00
lib: improve fletcher checksum validation OVERVIEW The checksum used in OSPF (rfc2328) is specified in rc905 annex B. There is an sample implementation in rfc1008 which forms the basis of the quagga implementation. This algorithm works perfectly when generating a checksum; however, validation is a bit problematic. The following LSA (generated by a non-quagga implementation) is received by quagga and marked with an invalid checksum; however, it passes both the rfc905 and rfc1008 validation checks. static uint8_t lsa_10_121_233_29[] = { 0x0e, 0x10, 0x02, 0x03, 0x09, 0x00, 0x35, 0x40, 0x0a, 0x79, 0xe9, 0x1d, 0x80, 0x00, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x1c, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x36, 0xb0 }; LS Type: Summary-LSA (IP network) LS Age: 3600 seconds Do Not Age: False Options: 0x02 (E) Link-State Advertisement Type: Summary-LSA (IP network) (3) Link State ID: 9.0.53.64 Advertising Router: 10.121.233.29 (10.121.233.29) LS Sequence Number: 0x80000003 LS Checksum: 0x008a Length: 28 Netmask: 255.255.255.224 Metric: 14000 You'll note that one byte of the checksum is 0x00; quagga would calculate the checksum as 0xff8a. It can be argued that the sourcing implementation generates an incorrect checksum; however, rfc905 indicates that, for 1's complement arithmetic, the value 255 shall be regarded as 0, thus either values are valid. EXPLANATION The quagga ospfd and ospf6d implementations operate by copying the PDU's existing checksum in a holding variable, calculating the checksum, and comparing the resulting checksum to the original. As a note, this implementation has the side effect of modifying the contents of the PDU. Evaluation of both rfc905 and rfc1008 shows that checksum validation should involve calculating the sum over the PDU and checking that both resulting C0 and C1 values are zero. This behavior is enacted in the rfc1008 implementation by calling encodecc with k = 0 (checksum offset); however, this functionality had been omitted from the quagga implementation. PATCH This patch adds the ability to call the quagga's fletcher_checksum() with a checksum offset value of 0xffff (aka FLETCHER_CHECKSUM_VALIDATE) which returns the sum over the buffer (a value of 0 indicates a valid checksum). This is similar to the mechanism in rfc1008 when called with k = 0. The patch also introduces ospf_lsa_checksum_valid(). ospf6d had it's own implementation of the fletcher checksum in ospf6_lsa_checksum(); it's the same algorithm as in fletcher_checksum(). This patch removes the local implementation in favor of the library's as well as creates and uses ospf6_lsa_checksum_valid(). quagga's ISIS implementation suffers from the same problem; however, I do not have the facilities to validate a fix to ISIS, thus this change has been left to the ISIS maintainers. The function iso_csum_verify() should be reduced to running the fletcher checksum over the buffer using an offset of 0. Signed-off-by: JR Rivers <jrrivers@cumulusnetworks.com> Reviewed-by: Scott Feldman <sfeldma@cumulusnetworks.com> Reviewed-by: Nolan Leake <nolan@cumulusnetworks.com> Reviewed-by: Ayan Banerjee <ayan@cumulusnetworks.com> Reviewed-by: Shrijeet Mukherjee <shm@cumulusnetworks.com> Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
2012-09-13 19:17:36 +02:00
/* All the offsets are zero-based. The offsets in the RFC1008 are
one-based. */
2002-12-13 21:15:29 +01:00
unsigned short ospf6_lsa_checksum(struct ospf6_lsa_header *lsa_header)
{
uint8_t *buffer = (uint8_t *)&lsa_header->type;
int type_offset =
buffer - (uint8_t *)&lsa_header->age; /* should be 2 */
2002-12-13 21:15:29 +01:00
lib: improve fletcher checksum validation OVERVIEW The checksum used in OSPF (rfc2328) is specified in rc905 annex B. There is an sample implementation in rfc1008 which forms the basis of the quagga implementation. This algorithm works perfectly when generating a checksum; however, validation is a bit problematic. The following LSA (generated by a non-quagga implementation) is received by quagga and marked with an invalid checksum; however, it passes both the rfc905 and rfc1008 validation checks. static uint8_t lsa_10_121_233_29[] = { 0x0e, 0x10, 0x02, 0x03, 0x09, 0x00, 0x35, 0x40, 0x0a, 0x79, 0xe9, 0x1d, 0x80, 0x00, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x1c, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x36, 0xb0 }; LS Type: Summary-LSA (IP network) LS Age: 3600 seconds Do Not Age: False Options: 0x02 (E) Link-State Advertisement Type: Summary-LSA (IP network) (3) Link State ID: 9.0.53.64 Advertising Router: 10.121.233.29 (10.121.233.29) LS Sequence Number: 0x80000003 LS Checksum: 0x008a Length: 28 Netmask: 255.255.255.224 Metric: 14000 You'll note that one byte of the checksum is 0x00; quagga would calculate the checksum as 0xff8a. It can be argued that the sourcing implementation generates an incorrect checksum; however, rfc905 indicates that, for 1's complement arithmetic, the value 255 shall be regarded as 0, thus either values are valid. EXPLANATION The quagga ospfd and ospf6d implementations operate by copying the PDU's existing checksum in a holding variable, calculating the checksum, and comparing the resulting checksum to the original. As a note, this implementation has the side effect of modifying the contents of the PDU. Evaluation of both rfc905 and rfc1008 shows that checksum validation should involve calculating the sum over the PDU and checking that both resulting C0 and C1 values are zero. This behavior is enacted in the rfc1008 implementation by calling encodecc with k = 0 (checksum offset); however, this functionality had been omitted from the quagga implementation. PATCH This patch adds the ability to call the quagga's fletcher_checksum() with a checksum offset value of 0xffff (aka FLETCHER_CHECKSUM_VALIDATE) which returns the sum over the buffer (a value of 0 indicates a valid checksum). This is similar to the mechanism in rfc1008 when called with k = 0. The patch also introduces ospf_lsa_checksum_valid(). ospf6d had it's own implementation of the fletcher checksum in ospf6_lsa_checksum(); it's the same algorithm as in fletcher_checksum(). This patch removes the local implementation in favor of the library's as well as creates and uses ospf6_lsa_checksum_valid(). quagga's ISIS implementation suffers from the same problem; however, I do not have the facilities to validate a fix to ISIS, thus this change has been left to the ISIS maintainers. The function iso_csum_verify() should be reduced to running the fletcher checksum over the buffer using an offset of 0. Signed-off-by: JR Rivers <jrrivers@cumulusnetworks.com> Reviewed-by: Scott Feldman <sfeldma@cumulusnetworks.com> Reviewed-by: Nolan Leake <nolan@cumulusnetworks.com> Reviewed-by: Ayan Banerjee <ayan@cumulusnetworks.com> Reviewed-by: Shrijeet Mukherjee <shm@cumulusnetworks.com> Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
2012-09-13 19:17:36 +02:00
/* Skip the AGE field */
uint16_t len = ntohs(lsa_header->length) - type_offset;
2002-12-13 21:15:29 +01:00
lib: improve fletcher checksum validation OVERVIEW The checksum used in OSPF (rfc2328) is specified in rc905 annex B. There is an sample implementation in rfc1008 which forms the basis of the quagga implementation. This algorithm works perfectly when generating a checksum; however, validation is a bit problematic. The following LSA (generated by a non-quagga implementation) is received by quagga and marked with an invalid checksum; however, it passes both the rfc905 and rfc1008 validation checks. static uint8_t lsa_10_121_233_29[] = { 0x0e, 0x10, 0x02, 0x03, 0x09, 0x00, 0x35, 0x40, 0x0a, 0x79, 0xe9, 0x1d, 0x80, 0x00, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x1c, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x36, 0xb0 }; LS Type: Summary-LSA (IP network) LS Age: 3600 seconds Do Not Age: False Options: 0x02 (E) Link-State Advertisement Type: Summary-LSA (IP network) (3) Link State ID: 9.0.53.64 Advertising Router: 10.121.233.29 (10.121.233.29) LS Sequence Number: 0x80000003 LS Checksum: 0x008a Length: 28 Netmask: 255.255.255.224 Metric: 14000 You'll note that one byte of the checksum is 0x00; quagga would calculate the checksum as 0xff8a. It can be argued that the sourcing implementation generates an incorrect checksum; however, rfc905 indicates that, for 1's complement arithmetic, the value 255 shall be regarded as 0, thus either values are valid. EXPLANATION The quagga ospfd and ospf6d implementations operate by copying the PDU's existing checksum in a holding variable, calculating the checksum, and comparing the resulting checksum to the original. As a note, this implementation has the side effect of modifying the contents of the PDU. Evaluation of both rfc905 and rfc1008 shows that checksum validation should involve calculating the sum over the PDU and checking that both resulting C0 and C1 values are zero. This behavior is enacted in the rfc1008 implementation by calling encodecc with k = 0 (checksum offset); however, this functionality had been omitted from the quagga implementation. PATCH This patch adds the ability to call the quagga's fletcher_checksum() with a checksum offset value of 0xffff (aka FLETCHER_CHECKSUM_VALIDATE) which returns the sum over the buffer (a value of 0 indicates a valid checksum). This is similar to the mechanism in rfc1008 when called with k = 0. The patch also introduces ospf_lsa_checksum_valid(). ospf6d had it's own implementation of the fletcher checksum in ospf6_lsa_checksum(); it's the same algorithm as in fletcher_checksum(). This patch removes the local implementation in favor of the library's as well as creates and uses ospf6_lsa_checksum_valid(). quagga's ISIS implementation suffers from the same problem; however, I do not have the facilities to validate a fix to ISIS, thus this change has been left to the ISIS maintainers. The function iso_csum_verify() should be reduced to running the fletcher checksum over the buffer using an offset of 0. Signed-off-by: JR Rivers <jrrivers@cumulusnetworks.com> Reviewed-by: Scott Feldman <sfeldma@cumulusnetworks.com> Reviewed-by: Nolan Leake <nolan@cumulusnetworks.com> Reviewed-by: Ayan Banerjee <ayan@cumulusnetworks.com> Reviewed-by: Shrijeet Mukherjee <shm@cumulusnetworks.com> Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
2012-09-13 19:17:36 +02:00
/* Checksum offset starts from "type" field, not the beginning of the
lsa_header struct. The offset is 14, rather than 16. */
int checksum_offset = (uint8_t *)&lsa_header->checksum - buffer;
lib: improve fletcher checksum validation OVERVIEW The checksum used in OSPF (rfc2328) is specified in rc905 annex B. There is an sample implementation in rfc1008 which forms the basis of the quagga implementation. This algorithm works perfectly when generating a checksum; however, validation is a bit problematic. The following LSA (generated by a non-quagga implementation) is received by quagga and marked with an invalid checksum; however, it passes both the rfc905 and rfc1008 validation checks. static uint8_t lsa_10_121_233_29[] = { 0x0e, 0x10, 0x02, 0x03, 0x09, 0x00, 0x35, 0x40, 0x0a, 0x79, 0xe9, 0x1d, 0x80, 0x00, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x1c, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x36, 0xb0 }; LS Type: Summary-LSA (IP network) LS Age: 3600 seconds Do Not Age: False Options: 0x02 (E) Link-State Advertisement Type: Summary-LSA (IP network) (3) Link State ID: 9.0.53.64 Advertising Router: 10.121.233.29 (10.121.233.29) LS Sequence Number: 0x80000003 LS Checksum: 0x008a Length: 28 Netmask: 255.255.255.224 Metric: 14000 You'll note that one byte of the checksum is 0x00; quagga would calculate the checksum as 0xff8a. It can be argued that the sourcing implementation generates an incorrect checksum; however, rfc905 indicates that, for 1's complement arithmetic, the value 255 shall be regarded as 0, thus either values are valid. EXPLANATION The quagga ospfd and ospf6d implementations operate by copying the PDU's existing checksum in a holding variable, calculating the checksum, and comparing the resulting checksum to the original. As a note, this implementation has the side effect of modifying the contents of the PDU. Evaluation of both rfc905 and rfc1008 shows that checksum validation should involve calculating the sum over the PDU and checking that both resulting C0 and C1 values are zero. This behavior is enacted in the rfc1008 implementation by calling encodecc with k = 0 (checksum offset); however, this functionality had been omitted from the quagga implementation. PATCH This patch adds the ability to call the quagga's fletcher_checksum() with a checksum offset value of 0xffff (aka FLETCHER_CHECKSUM_VALIDATE) which returns the sum over the buffer (a value of 0 indicates a valid checksum). This is similar to the mechanism in rfc1008 when called with k = 0. The patch also introduces ospf_lsa_checksum_valid(). ospf6d had it's own implementation of the fletcher checksum in ospf6_lsa_checksum(); it's the same algorithm as in fletcher_checksum(). This patch removes the local implementation in favor of the library's as well as creates and uses ospf6_lsa_checksum_valid(). quagga's ISIS implementation suffers from the same problem; however, I do not have the facilities to validate a fix to ISIS, thus this change has been left to the ISIS maintainers. The function iso_csum_verify() should be reduced to running the fletcher checksum over the buffer using an offset of 0. Signed-off-by: JR Rivers <jrrivers@cumulusnetworks.com> Reviewed-by: Scott Feldman <sfeldma@cumulusnetworks.com> Reviewed-by: Nolan Leake <nolan@cumulusnetworks.com> Reviewed-by: Ayan Banerjee <ayan@cumulusnetworks.com> Reviewed-by: Shrijeet Mukherjee <shm@cumulusnetworks.com> Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
2012-09-13 19:17:36 +02:00
return (unsigned short)fletcher_checksum(buffer, len, checksum_offset);
}
2002-12-13 21:15:29 +01:00
lib: improve fletcher checksum validation OVERVIEW The checksum used in OSPF (rfc2328) is specified in rc905 annex B. There is an sample implementation in rfc1008 which forms the basis of the quagga implementation. This algorithm works perfectly when generating a checksum; however, validation is a bit problematic. The following LSA (generated by a non-quagga implementation) is received by quagga and marked with an invalid checksum; however, it passes both the rfc905 and rfc1008 validation checks. static uint8_t lsa_10_121_233_29[] = { 0x0e, 0x10, 0x02, 0x03, 0x09, 0x00, 0x35, 0x40, 0x0a, 0x79, 0xe9, 0x1d, 0x80, 0x00, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x1c, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x36, 0xb0 }; LS Type: Summary-LSA (IP network) LS Age: 3600 seconds Do Not Age: False Options: 0x02 (E) Link-State Advertisement Type: Summary-LSA (IP network) (3) Link State ID: 9.0.53.64 Advertising Router: 10.121.233.29 (10.121.233.29) LS Sequence Number: 0x80000003 LS Checksum: 0x008a Length: 28 Netmask: 255.255.255.224 Metric: 14000 You'll note that one byte of the checksum is 0x00; quagga would calculate the checksum as 0xff8a. It can be argued that the sourcing implementation generates an incorrect checksum; however, rfc905 indicates that, for 1's complement arithmetic, the value 255 shall be regarded as 0, thus either values are valid. EXPLANATION The quagga ospfd and ospf6d implementations operate by copying the PDU's existing checksum in a holding variable, calculating the checksum, and comparing the resulting checksum to the original. As a note, this implementation has the side effect of modifying the contents of the PDU. Evaluation of both rfc905 and rfc1008 shows that checksum validation should involve calculating the sum over the PDU and checking that both resulting C0 and C1 values are zero. This behavior is enacted in the rfc1008 implementation by calling encodecc with k = 0 (checksum offset); however, this functionality had been omitted from the quagga implementation. PATCH This patch adds the ability to call the quagga's fletcher_checksum() with a checksum offset value of 0xffff (aka FLETCHER_CHECKSUM_VALIDATE) which returns the sum over the buffer (a value of 0 indicates a valid checksum). This is similar to the mechanism in rfc1008 when called with k = 0. The patch also introduces ospf_lsa_checksum_valid(). ospf6d had it's own implementation of the fletcher checksum in ospf6_lsa_checksum(); it's the same algorithm as in fletcher_checksum(). This patch removes the local implementation in favor of the library's as well as creates and uses ospf6_lsa_checksum_valid(). quagga's ISIS implementation suffers from the same problem; however, I do not have the facilities to validate a fix to ISIS, thus this change has been left to the ISIS maintainers. The function iso_csum_verify() should be reduced to running the fletcher checksum over the buffer using an offset of 0. Signed-off-by: JR Rivers <jrrivers@cumulusnetworks.com> Reviewed-by: Scott Feldman <sfeldma@cumulusnetworks.com> Reviewed-by: Nolan Leake <nolan@cumulusnetworks.com> Reviewed-by: Ayan Banerjee <ayan@cumulusnetworks.com> Reviewed-by: Shrijeet Mukherjee <shm@cumulusnetworks.com> Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
2012-09-13 19:17:36 +02:00
int ospf6_lsa_checksum_valid(struct ospf6_lsa_header *lsa_header)
{
uint8_t *buffer = (uint8_t *)&lsa_header->type;
int type_offset =
buffer - (uint8_t *)&lsa_header->age; /* should be 2 */
2002-12-13 21:15:29 +01:00
lib: improve fletcher checksum validation OVERVIEW The checksum used in OSPF (rfc2328) is specified in rc905 annex B. There is an sample implementation in rfc1008 which forms the basis of the quagga implementation. This algorithm works perfectly when generating a checksum; however, validation is a bit problematic. The following LSA (generated by a non-quagga implementation) is received by quagga and marked with an invalid checksum; however, it passes both the rfc905 and rfc1008 validation checks. static uint8_t lsa_10_121_233_29[] = { 0x0e, 0x10, 0x02, 0x03, 0x09, 0x00, 0x35, 0x40, 0x0a, 0x79, 0xe9, 0x1d, 0x80, 0x00, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x1c, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x36, 0xb0 }; LS Type: Summary-LSA (IP network) LS Age: 3600 seconds Do Not Age: False Options: 0x02 (E) Link-State Advertisement Type: Summary-LSA (IP network) (3) Link State ID: 9.0.53.64 Advertising Router: 10.121.233.29 (10.121.233.29) LS Sequence Number: 0x80000003 LS Checksum: 0x008a Length: 28 Netmask: 255.255.255.224 Metric: 14000 You'll note that one byte of the checksum is 0x00; quagga would calculate the checksum as 0xff8a. It can be argued that the sourcing implementation generates an incorrect checksum; however, rfc905 indicates that, for 1's complement arithmetic, the value 255 shall be regarded as 0, thus either values are valid. EXPLANATION The quagga ospfd and ospf6d implementations operate by copying the PDU's existing checksum in a holding variable, calculating the checksum, and comparing the resulting checksum to the original. As a note, this implementation has the side effect of modifying the contents of the PDU. Evaluation of both rfc905 and rfc1008 shows that checksum validation should involve calculating the sum over the PDU and checking that both resulting C0 and C1 values are zero. This behavior is enacted in the rfc1008 implementation by calling encodecc with k = 0 (checksum offset); however, this functionality had been omitted from the quagga implementation. PATCH This patch adds the ability to call the quagga's fletcher_checksum() with a checksum offset value of 0xffff (aka FLETCHER_CHECKSUM_VALIDATE) which returns the sum over the buffer (a value of 0 indicates a valid checksum). This is similar to the mechanism in rfc1008 when called with k = 0. The patch also introduces ospf_lsa_checksum_valid(). ospf6d had it's own implementation of the fletcher checksum in ospf6_lsa_checksum(); it's the same algorithm as in fletcher_checksum(). This patch removes the local implementation in favor of the library's as well as creates and uses ospf6_lsa_checksum_valid(). quagga's ISIS implementation suffers from the same problem; however, I do not have the facilities to validate a fix to ISIS, thus this change has been left to the ISIS maintainers. The function iso_csum_verify() should be reduced to running the fletcher checksum over the buffer using an offset of 0. Signed-off-by: JR Rivers <jrrivers@cumulusnetworks.com> Reviewed-by: Scott Feldman <sfeldma@cumulusnetworks.com> Reviewed-by: Nolan Leake <nolan@cumulusnetworks.com> Reviewed-by: Ayan Banerjee <ayan@cumulusnetworks.com> Reviewed-by: Shrijeet Mukherjee <shm@cumulusnetworks.com> Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
2012-09-13 19:17:36 +02:00
/* Skip the AGE field */
uint16_t len = ntohs(lsa_header->length) - type_offset;
2002-12-13 21:15:29 +01:00
lib: improve fletcher checksum validation OVERVIEW The checksum used in OSPF (rfc2328) is specified in rc905 annex B. There is an sample implementation in rfc1008 which forms the basis of the quagga implementation. This algorithm works perfectly when generating a checksum; however, validation is a bit problematic. The following LSA (generated by a non-quagga implementation) is received by quagga and marked with an invalid checksum; however, it passes both the rfc905 and rfc1008 validation checks. static uint8_t lsa_10_121_233_29[] = { 0x0e, 0x10, 0x02, 0x03, 0x09, 0x00, 0x35, 0x40, 0x0a, 0x79, 0xe9, 0x1d, 0x80, 0x00, 0x00, 0x03, 0x00, 0x8a, 0x00, 0x1c, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x36, 0xb0 }; LS Type: Summary-LSA (IP network) LS Age: 3600 seconds Do Not Age: False Options: 0x02 (E) Link-State Advertisement Type: Summary-LSA (IP network) (3) Link State ID: 9.0.53.64 Advertising Router: 10.121.233.29 (10.121.233.29) LS Sequence Number: 0x80000003 LS Checksum: 0x008a Length: 28 Netmask: 255.255.255.224 Metric: 14000 You'll note that one byte of the checksum is 0x00; quagga would calculate the checksum as 0xff8a. It can be argued that the sourcing implementation generates an incorrect checksum; however, rfc905 indicates that, for 1's complement arithmetic, the value 255 shall be regarded as 0, thus either values are valid. EXPLANATION The quagga ospfd and ospf6d implementations operate by copying the PDU's existing checksum in a holding variable, calculating the checksum, and comparing the resulting checksum to the original. As a note, this implementation has the side effect of modifying the contents of the PDU. Evaluation of both rfc905 and rfc1008 shows that checksum validation should involve calculating the sum over the PDU and checking that both resulting C0 and C1 values are zero. This behavior is enacted in the rfc1008 implementation by calling encodecc with k = 0 (checksum offset); however, this functionality had been omitted from the quagga implementation. PATCH This patch adds the ability to call the quagga's fletcher_checksum() with a checksum offset value of 0xffff (aka FLETCHER_CHECKSUM_VALIDATE) which returns the sum over the buffer (a value of 0 indicates a valid checksum). This is similar to the mechanism in rfc1008 when called with k = 0. The patch also introduces ospf_lsa_checksum_valid(). ospf6d had it's own implementation of the fletcher checksum in ospf6_lsa_checksum(); it's the same algorithm as in fletcher_checksum(). This patch removes the local implementation in favor of the library's as well as creates and uses ospf6_lsa_checksum_valid(). quagga's ISIS implementation suffers from the same problem; however, I do not have the facilities to validate a fix to ISIS, thus this change has been left to the ISIS maintainers. The function iso_csum_verify() should be reduced to running the fletcher checksum over the buffer using an offset of 0. Signed-off-by: JR Rivers <jrrivers@cumulusnetworks.com> Reviewed-by: Scott Feldman <sfeldma@cumulusnetworks.com> Reviewed-by: Nolan Leake <nolan@cumulusnetworks.com> Reviewed-by: Ayan Banerjee <ayan@cumulusnetworks.com> Reviewed-by: Shrijeet Mukherjee <shm@cumulusnetworks.com> Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
2012-09-13 19:17:36 +02:00
return (fletcher_checksum(buffer, len, FLETCHER_CHECKSUM_VALIDATE)
== 0);
2002-12-13 21:15:29 +01:00
}
void ospf6_lsa_init(void)
{
ospf6_lsa_handler_vector = vector_init(0);
ospf6_install_lsa_handler(&unknown_handler);
2002-12-13 21:15:29 +01:00
}
void ospf6_lsa_terminate(void)
{
vector_free(ospf6_lsa_handler_vector);
}
static char *ospf6_lsa_handler_name(const struct ospf6_lsa_handler *h)
{
static char buf[64];
unsigned int i;
unsigned int size = strlen(h->lh_name);
if (!strcmp(h->lh_name, "unknown")
&& h->lh_type != OSPF6_LSTYPE_UNKNOWN) {
snprintf(buf, sizeof(buf), "%#04hx", h->lh_type);
return buf;
}
for (i = 0; i < MIN(size, sizeof(buf)); i++) {
if (!islower((unsigned char)h->lh_name[i]))
buf[i] = tolower((unsigned char)h->lh_name[i]);
else
buf[i] = h->lh_name[i];
}
buf[size] = '\0';
return buf;
}
2002-12-13 21:15:29 +01:00
DEFUN (debug_ospf6_lsa_type,
debug_ospf6_lsa_hex_cmd,
"debug ospf6 lsa <router|network|inter-prefix|inter-router|as-external|link|intra-prefix|unknown> [<originate|examine|flooding>]",
DEBUG_STR
OSPF6_STR
"Debug Link State Advertisements (LSAs)\n"
"Display Router LSAs\n"
"Display Network LSAs\n"
"Display Inter-Area-Prefix LSAs\n"
"Display Inter-Router LSAs\n"
"Display As-External LSAs\n"
"Display Link LSAs\n"
"Display Intra-Area-Prefix LSAs\n"
"Display LSAs of unknown origin\n"
"Display details of LSAs\n"
"Dump LSAs\n"
"Display LSA's internal information\n")
{
int idx_lsa = 3;
int idx_type = 4;
unsigned int i;
struct ospf6_lsa_handler *handler = NULL;
for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) {
handler = vector_slot(ospf6_lsa_handler_vector, i);
if (handler == NULL)
continue;
if (strncmp(argv[idx_lsa]->arg, ospf6_lsa_handler_name(handler),
strlen(argv[idx_lsa]->arg))
== 0)
break;
if (!strcasecmp(argv[idx_lsa]->arg, handler->lh_name))
break;
handler = NULL;
}
if (handler == NULL)
handler = &unknown_handler;
if (argc == 5) {
if (strmatch(argv[idx_type]->text, "originate"))
SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ORIGINATE);
else if (strmatch(argv[idx_type]->text, "examine"))
SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_EXAMIN);
else if (strmatch(argv[idx_type]->text, "flooding"))
SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_FLOOD);
} else
SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG);
return CMD_SUCCESS;
}
DEFUN (no_debug_ospf6_lsa_type,
no_debug_ospf6_lsa_hex_cmd,
"no debug ospf6 lsa <router|network|inter-prefix|inter-router|as-external|link|intra-prefix|unknown> [<originate|examine|flooding>]",
NO_STR
DEBUG_STR
OSPF6_STR
"Debug Link State Advertisements (LSAs)\n"
"Display Router LSAs\n"
"Display Network LSAs\n"
"Display Inter-Area-Prefix LSAs\n"
"Display Inter-Router LSAs\n"
"Display As-External LSAs\n"
"Display Link LSAs\n"
"Display Intra-Area-Prefix LSAs\n"
"Display LSAs of unknown origin\n"
"Display details of LSAs\n"
"Dump LSAs\n"
"Display LSA's internal information\n")
{
int idx_lsa = 4;
int idx_type = 5;
unsigned int i;
struct ospf6_lsa_handler *handler = NULL;
for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) {
handler = vector_slot(ospf6_lsa_handler_vector, i);
if (handler == NULL)
continue;
if (strncmp(argv[idx_lsa]->arg, ospf6_lsa_handler_name(handler),
strlen(argv[idx_lsa]->arg))
== 0)
break;
if (!strcasecmp(argv[idx_lsa]->arg, handler->lh_name))
break;
2002-12-13 21:15:29 +01:00
}
if (handler == NULL)
return CMD_SUCCESS;
if (argc == 6) {
if (strmatch(argv[idx_type]->text, "originate"))
UNSET_FLAG(handler->lh_debug,
OSPF6_LSA_DEBUG_ORIGINATE);
if (strmatch(argv[idx_type]->text, "examine"))
UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_EXAMIN);
if (strmatch(argv[idx_type]->text, "flooding"))
UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_FLOOD);
} else
UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG);
return CMD_SUCCESS;
}
void install_element_ospf6_debug_lsa(void)
{
install_element(ENABLE_NODE, &debug_ospf6_lsa_hex_cmd);
install_element(ENABLE_NODE, &no_debug_ospf6_lsa_hex_cmd);
install_element(CONFIG_NODE, &debug_ospf6_lsa_hex_cmd);
install_element(CONFIG_NODE, &no_debug_ospf6_lsa_hex_cmd);
}
int config_write_ospf6_debug_lsa(struct vty *vty)
{
unsigned int i;
const struct ospf6_lsa_handler *handler;
for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) {
handler = vector_slot(ospf6_lsa_handler_vector, i);
if (handler == NULL)
continue;
if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG))
vty_out(vty, "debug ospf6 lsa %s\n",
ospf6_lsa_handler_name(handler));
if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ORIGINATE))
vty_out(vty, "debug ospf6 lsa %s originate\n",
ospf6_lsa_handler_name(handler));
if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_EXAMIN))
vty_out(vty, "debug ospf6 lsa %s examine\n",
ospf6_lsa_handler_name(handler));
if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_FLOOD))
vty_out(vty, "debug ospf6 lsa %s flooding\n",
ospf6_lsa_handler_name(handler));
}
2002-12-13 21:15:29 +01:00
return 0;
}