fix OSPF LSA generation and checksum

This commit is contained in:
Christian Giese
2023-08-25 11:46:35 +00:00
parent 3572c13ff6
commit 00b037a0ef
6 changed files with 252 additions and 81 deletions
+1
View File
@@ -12,6 +12,7 @@
#include "../bbl.h"
#include "ospf_def.h"
#include "ospf_utils.h"
#include "ospf_checksum.h"
#include "ospf_pdu.h"
#include "ospf_interface.h"
#include "ospf_neighbor.h"
+72
View File
@@ -0,0 +1,72 @@
/*
* BNG Blaster (BBL) - OSPF Checksum
*
* Christian Giese, July 2022
*
* Copyright (C) 2020-2023, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ospf.h"
/* Fletcher Checksum -- Refer to RFC1008. */
#define MODX 4102 /* 5802 should be fine */
/**
* ospf_checksum_fletcher16
*
* This function is based on the implementation from
* the famous FRR project (https://github.com/FRRouting/frr).
*
* To be consistent, offset is 0-based index, rather than the 1-based
* index required in the specification ISO 8473, Annex C.1.
*/
uint16_t
ospf_checksum_fletcher16(uint8_t *buf, uint16_t len, uint16_t offset)
{
uint8_t *p;
uint16_t *csum;
int32_t x, y, c0, c1;
uint32_t partial_len, i, left = len;
p = buf;
c0 = 0;
c1 = 0;
/* Zero the checksum in the packet. */
csum = (uint16_t*)(buf + offset);
*csum = 0;
while (left != 0) {
if(left < MODX) {
partial_len = left;
} else {
partial_len = MODX;
}
for (i = 0; i < partial_len; i++) {
c0 = c0 + *(p++);
c1 += c0;
}
c0 = c0 % 255;
c1 = c1 % 255;
left -= partial_len;
}
/* The cast is important, to ensure the mod
* is taken as a signed value. */
x = (int)((len - offset - 1) * c0 - c1) % 255;
if(x <= 0) {
x += 255;
}
y = 510 - c0 - x;
if(y > 255) {
y -= 255;
}
/* Now we write this to the packet.
* We could skip this step too, since the checksum returned would
* be stored into the checksum field by the caller. */
buf[offset] = x;
buf[offset + 1] = y;
return htobe16((x << 8) | (y & 0xFF));
}
+15
View File
@@ -0,0 +1,15 @@
/*
* BNG Blaster (BBL) - OSPF Checksum
*
* Christian Giese, July 2022
*
* Copyright (C) 2020-2023, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_OSPF_CHECKSUM_H__
#define __BBL_OSPF_CHECKSUM_H__
uint16_t
ospf_checksum_fletcher16(uint8_t *buf, uint16_t len, uint16_t offset);
#endif
+2 -2
View File
@@ -15,7 +15,7 @@
#define OSPF_DEFAULT_DEAD_INTERVAL 40
#define OSPF_DEFAULT_LSA_RETRY_IVL 5
#define OSPF_DEFAULT_ROUTER_PRIORITY 64
#define OSPF_DEFAULT_METRIC 10
#define OSPF_DEFAULT_METRIC 1
#define OSPF_LSA_TYPES 11
@@ -58,7 +58,7 @@
#define OSPF_LSA_REFRESH_TIME 1800 /* 30 minutes */
#define OSPF_LSA_MAX_AGE 3600 /* 1 hour */
#define OSPF_LSA_MAX_AGE_DIFF 900 /* 15 minutes */
#define OSPF_LSA_SEQ_INIT 0x80000001
#define OSPF_LSA_SEQ_INIT 0x80000000
#define OSPF_LSA_SEQ_MAX 0x7fffffff
#define OSPF_LSA_BORDER_ROUTER 0x01
+7 -1
View File
@@ -228,9 +228,15 @@ ospf_interface_init(bbl_network_interface_s *interface,
ospf_interface->instance = ospf;
ospf_interface->version = version;
ospf_interface->type = interface_type;
if(version == OSPF_VERSION_2) {
ospf_interface->metric = network_config->ospfv2_metric;
} else {
ospf_interface->metric = network_config->ospfv3_metric;
}
ospf_interface->next = ospf->interfaces;
ospf->interfaces = ospf_interface;
if(interface_type == OSPF_INTERFACE_P2P ||
interface_type == OSPF_INTERFACE_VIRTUAL) {
ospf_interface_update_state(ospf_interface, OSPF_IFSTATE_P2P);
+155 -78
View File
@@ -260,7 +260,6 @@ ospf_lsa_gc_job(timer_s *timer)
void
ospf_lsa_flood(ospf_lsa_s *lsa)
{
ospf_interface_s *ospf_interface;
ospf_neighbor_s *ospf_neighbor;
ospf_lsa_tree_entry_s *entry;
@@ -276,7 +275,7 @@ ospf_lsa_flood(ospf_lsa_s *lsa)
/* Add to neighbors retry list. */
ospf_neighbor = ospf_interface->neighbors;
while(ospf_neighbor) {
if(ospf_neighbor->state >= OSPF_NBSTATE_EXCHANGE && lsa->source.router_id != ospf_neighbor->router_id) {
if(ospf_neighbor->state > OSPF_NBSTATE_EXSTART && lsa->source.router_id != ospf_neighbor->router_id) {
flood_interface = true;
entry = ospf_lsa_tree_add(lsa, NULL, ospf_neighbor->lsa_retry_tree[lsa->type]);
if(entry) {
@@ -321,6 +320,7 @@ static void
ospf_lsa_update_hdr(ospf_lsa_s *lsa)
{
ospf_lsa_header_s *hdr;
uint16_t checksum = 0;
assert(lsa->lsa_len >= sizeof(ospf_lsa_header_s));
if(lsa->lsa_len < sizeof(ospf_lsa_header_s)) {
@@ -330,8 +330,27 @@ ospf_lsa_update_hdr(ospf_lsa_s *lsa)
hdr = (ospf_lsa_header_s*)lsa->lsa;
hdr->age = htobe16(lsa->age);
hdr->seq = htobe32(lsa->seq);
hdr->checksum = 0;
hdr->checksum = bbl_checksum(lsa->lsa+OSPF_LSA_AGE_LEN, lsa->lsa_len-OSPF_LSA_AGE_LEN);
checksum = ospf_checksum_fletcher16(lsa->lsa+OSPF_LSA_AGE_LEN, lsa->lsa_len-OSPF_LSA_AGE_LEN, 14);
hdr->checksum = checksum;
}
static bool
ospf_lsa_verify_checksum(ospf_lsa_header_s *hdr)
{
uint16_t checksum = 0;
uint16_t checksum_orig = hdr->checksum;
uint16_t len = be16toh(hdr->length);
if(len < sizeof(ospf_lsa_header_s)) return false;
checksum = ospf_checksum_fletcher16(&hdr->options, len-OSPF_LSA_AGE_LEN, 14);
hdr->checksum = checksum_orig;
if(checksum == checksum_orig) {
return true;
} else {
return false;
}
}
static void
@@ -412,62 +431,101 @@ ospf_lsa_purge_all_external(ospf_instance_s *ospf_instance)
}
}
static bool
uint16_t
ospf_lsa_add_interface(ospf_lsa_s *lsa, ospf_interface_s *ospf_interface)
{
ospf_neighbor_s *neighbor = ospf_interface->neighbors;
ospf_neighbor_s *ospf_neighbor = ospf_interface->neighbors;
ospf_lsa_link_s *link;
if(lsa->lsa_len + sizeof(ospf_lsa_link_s) > lsa->lsa_buf_len) {
return false;
}
link = (ospf_lsa_link_s*)(lsa->lsa+lsa->lsa_len);
uint16_t links = 0;
if(ospf_interface->type == OSPF_INTERFACE_P2P) {
link->link_id = ospf_interface->interface->ip.address & ipv4_len_to_mask(ospf_interface->interface->ip.len);
link->link_data = ipv4_len_to_mask(ospf_interface->interface->ip.len);
link->type = OSPF_LSA_LINK_STUB;
} else if(ospf_interface->type == OSPF_INTERFACE_BROADCAST) {
} else {
return false;
/* We need space for up to 2 links per interface! */
if(lsa->lsa_len + (sizeof(ospf_lsa_link_s)*2) > lsa->lsa_buf_len) {
return 0;
}
if(ospf_interface->type == OSPF_INTERFACE_P2P) {
if(!(neighbor && neighbor->state == OSPF_NBSTATE_FULL)) {
return false;
}
link->link_id = neighbor->router_id;
link->link_data = ospf_interface->interface->ip.address;
link->type = OSPF_LSA_LINK_P2P;
} else if(ospf_interface->type == OSPF_INTERFACE_BROADCAST) {
while(neighbor) {
if(neighbor->state == OSPF_NBSTATE_FULL &&
(ospf_interface->state == OSPF_IFSTATE_DR ||
ospf_interface->dr == neighbor->router_id)) {
break;
switch(ospf_interface->state) {
case OSPF_IFSTATE_P2P:
if(ospf_interface->neighbors_full) {
link = (ospf_lsa_link_s*)(lsa->lsa+lsa->lsa_len);
link->link_id = ospf_neighbor->router_id;
link->link_data = ospf_interface->interface->ip.address;
link->type = OSPF_LSA_LINK_P2P;
link->tos = 0;
link->metric = htobe16(ospf_interface->metric);
lsa->lsa_len += sizeof(ospf_lsa_link_s);
links++;
}
neighbor = neighbor->next;
}
if(neighbor) {
link->link_id = ospf_interface->dr;
link->link_data = ospf_interface->interface->ip.address;
link->type = OSPF_LSA_LINK_TRANSIT;
} else {
link = (ospf_lsa_link_s*)(lsa->lsa+lsa->lsa_len);
link->link_id = ospf_interface->interface->ip.address & ipv4_len_to_mask(ospf_interface->interface->ip.len);
link->link_data = ipv4_len_to_mask(ospf_interface->interface->ip.len);
link->type = OSPF_LSA_LINK_STUB;
}
} else {
return false;
link->tos = 0;
link->metric = htobe16(ospf_interface->metric);
lsa->lsa_len += sizeof(ospf_lsa_link_s);
links++;
break;
case OSPF_IFSTATE_WAITING:
link = (ospf_lsa_link_s*)(lsa->lsa+lsa->lsa_len);
link->link_id = ospf_interface->interface->ip.address & ipv4_len_to_mask(ospf_interface->interface->ip.len);
link->link_data = ipv4_len_to_mask(ospf_interface->interface->ip.len);
link->type = OSPF_LSA_LINK_STUB;
link->tos = 0;
link->metric = htobe16(ospf_interface->metric);
lsa->lsa_len += sizeof(ospf_lsa_link_s);
links++;
break;
case OSPF_IFSTATE_DR:
if(ospf_interface->neighbors_full) {
link = (ospf_lsa_link_s*)(lsa->lsa+lsa->lsa_len);
link->link_id = ospf_interface->interface->ip.address;
link->link_data = ospf_interface->interface->ip.address;
link->type = OSPF_LSA_LINK_TRANSIT;
link->tos = 0;
link->metric = htobe16(ospf_interface->metric);
lsa->lsa_len += sizeof(ospf_lsa_link_s);
links++;
} else {
link = (ospf_lsa_link_s*)(lsa->lsa+lsa->lsa_len);
link->link_id = ospf_interface->interface->ip.address & ipv4_len_to_mask(ospf_interface->interface->ip.len);
link->link_data = ipv4_len_to_mask(ospf_interface->interface->ip.len);
link->type = OSPF_LSA_LINK_STUB;
link->tos = 0;
link->metric = htobe16(ospf_interface->metric);
lsa->lsa_len += sizeof(ospf_lsa_link_s);
links++;
}
if(lsa->lsa_len + sizeof(ospf_lsa_link_s) > lsa->lsa_buf_len) {
return links;
}
break;
case OSPF_IFSTATE_BACKUP:
case OSPF_IFSTATE_DR_OTHER:
while(ospf_neighbor) {
if(ospf_neighbor->state == OSPF_NBSTATE_FULL &&
ospf_neighbor->router_id == ospf_interface->dr) {
link = (ospf_lsa_link_s*)(lsa->lsa+lsa->lsa_len);
link->link_id = ospf_neighbor->ipv4;
link->link_data = ospf_interface->interface->ip.address;
link->type = OSPF_LSA_LINK_TRANSIT;
lsa->lsa_len += sizeof(ospf_lsa_link_s);
links++;
break;
}
ospf_neighbor = ospf_neighbor->next;
}
link = (ospf_lsa_link_s*)(lsa->lsa+lsa->lsa_len);
link->link_id = ospf_interface->interface->ip.address & ipv4_len_to_mask(ospf_interface->interface->ip.len);
link->link_data = ipv4_len_to_mask(ospf_interface->interface->ip.len);
link->type = OSPF_LSA_LINK_STUB;
link->tos = 0;
link->metric = htobe16(ospf_interface->metric);
lsa->lsa_len += sizeof(ospf_lsa_link_s);
links++;
break;
default:
break;
}
link->tos = 0;
link->metric = htobe16(ospf_interface->metric);
lsa->lsa_len += sizeof(ospf_lsa_link_s);
return true;
return links;
}
/**
@@ -508,6 +566,10 @@ ospf_lsa_self_update(ospf_instance_s *ospf_instance)
} else {
/* Create new LSA. */
lsa = ospf_lsa_new(OSPF_LSA_TYPE_1, &key, ospf_instance);
lsa->seq = OSPF_LSA_SEQ_INIT;
lsa->source.type = OSPF_SOURCE_SELF;
lsa->lsa = malloc(OSPF_MAX_SELF_LSA_LEN);
lsa->lsa_buf_len = OSPF_MAX_SELF_LSA_LEN;
result = hb_tree_insert(ospf_instance->lsdb[OSPF_LSA_TYPE_1], &lsa->key);
assert(result.inserted);
if(result.inserted) {
@@ -517,27 +579,14 @@ ospf_lsa_self_update(ospf_instance_s *ospf_instance)
return false;
}
}
if(lsa->lsa_buf_len < OSPF_MAX_SELF_LSA_LEN) {
if(lsa->lsa) free(lsa->lsa);
lsa->lsa = malloc(OSPF_MAX_SELF_LSA_LEN);
lsa->lsa_buf_len = OSPF_MAX_SELF_LSA_LEN;
}
lsa->lsa_len = OSPF_LSA_HDR_LEN;
lsa->source.type = OSPF_SOURCE_SELF;
lsa->seq++;
lsa->instance = ospf_instance;
lsa->deleted = false;
lsa->deleted = false;
hdr = (ospf_lsa_header_s*)lsa->lsa;
hdr->age = htobe16(lsa->age);
hdr->options = options;
hdr->type = OSPF_LSA_TYPE_1;
hdr->id = key.id;
hdr->router = key.router;
hdr->seq = htobe32(lsa->seq);
hdr->checksum = 0;
hdr->length = 0;
/* Set external and border router bits. */
*(lsa->lsa+lsa->lsa_len++) = OSPF_LSA_BORDER_ROUTER|OSPF_LSA_EXTERNAL_ROUTER;
@@ -549,19 +598,17 @@ ospf_lsa_self_update(ospf_instance_s *ospf_instance)
/* Add loopback */
link = (ospf_lsa_link_s*)(lsa->lsa+lsa->lsa_len);
lsa->lsa_len += sizeof(ospf_lsa_link_s);
link->link_id = config->router_id;
link->link_data = 0xffffffff;
link->type = OSPF_LSA_LINK_STUB;
link->tos = 0;
link->metric = 0;
lsa->lsa_len += sizeof(ospf_lsa_link_s);
links++;
/* Add OSPF neighbor interfaces */
while(ospf_interface && lsa->lsa_len + sizeof(ospf_lsa_link_s) <= lsa->lsa_buf_len) {
if(ospf_lsa_add_interface(lsa, ospf_interface)) {
links++;
}
links += ospf_lsa_add_interface(lsa, ospf_interface);
ospf_interface = ospf_interface->next;
}
@@ -569,14 +616,13 @@ ospf_lsa_self_update(ospf_instance_s *ospf_instance)
external_connection = config->external_connection;
while(external_connection && lsa->lsa_len + sizeof(ospf_lsa_link_s) <= lsa->lsa_buf_len) {
link = (ospf_lsa_link_s*)(lsa->lsa+lsa->lsa_len);
lsa->lsa_len += sizeof(ospf_lsa_link_s);
link->link_id = external_connection->router_id;
link->link_data = external_connection->ipv4.address;
link->type = OSPF_LSA_LINK_P2P;
link->tos = 0;
link->metric = external_connection->metric;
lsa->lsa_len += sizeof(ospf_lsa_link_s);
links++;
external_connection = external_connection->next;
}
@@ -900,8 +946,11 @@ ospf_lsa_update_handler_rx(ospf_interface_s *ospf_interface,
void **search = NULL;
dict_insert_result result;
uint32_t lsa_id;
uint32_t lsa_router;
uint32_t lsa_count;
uint16_t lsa_len;
uint8_t lsa_type;
int lsa_compare;
struct timespec now;
@@ -938,7 +987,11 @@ ospf_lsa_update_handler_rx(ospf_interface_s *ospf_interface,
hdr = (ospf_lsa_header_s*)OSPF_PDU_CURSOR(pdu);
key = (ospf_lsa_key_s*)&hdr->id;
if(hdr->type < OSPF_LSA_TYPE_1 || hdr->type > OSPF_LSA_TYPE_11) {
lsa_type = hdr->type;
lsa_id = hdr->id;
lsa_router = hdr->router;
if(lsa_type < OSPF_LSA_TYPE_1 || lsa_type > OSPF_LSA_TYPE_11) {
ospf_rx_error(interface, pdu, "decode (invalid LSA type)");
return;
}
@@ -947,19 +1000,23 @@ ospf_lsa_update_handler_rx(ospf_interface_s *ospf_interface,
ospf_rx_error(interface, pdu, "decode (invalid LSA len)");
return;
}
if(!ospf_lsa_verify_checksum(hdr)) {
ospf_rx_error(interface, pdu, "decode (invalid LSA checksum)");
return;
}
OSPF_PDU_CURSOR_INC(pdu, lsa_len);
lsa_count--;
search = hb_tree_search(ospf_neighbor->lsa_request_tree[hdr->type], key);
search = hb_tree_search(ospf_neighbor->lsa_request_tree[lsa_type], key);
if(search) {
entry = *search;
if(ospf_lsa_compare((ospf_lsa_header_s*)&entry->hdr, hdr) != 1) {
ospf_lsa_tree_remove(&entry->key, ospf_neighbor->lsa_request_tree[hdr->type]);
ospf_lsa_tree_remove(&entry->key, ospf_neighbor->lsa_request_tree[lsa_type]);
}
}
search = hb_tree_search(ospf_instance->lsdb[hdr->type], key);
search = hb_tree_search(ospf_instance->lsdb[lsa_type], key);
if(search) {
lsa = *search;
ospf_lsa_update_age(lsa, &now);
@@ -968,7 +1025,7 @@ ospf_lsa_update_handler_rx(ospf_interface_s *ospf_interface,
/* LOCAL LSA IS NEWER */
if(!(lsa->seq == OSPF_LSA_SEQ_MAX && lsa->age >= OSPF_LSA_MAX_AGE)) {
/* Send direct LSA update. */
ospf_lsa_tree_add(lsa, NULL, ospf_neighbor->lsa_update_tree[lsa->type]);
ospf_lsa_tree_add(lsa, NULL, ospf_neighbor->lsa_update_tree[lsa_type]);
}
/* Next LSA from update ... */
continue;
@@ -979,20 +1036,29 @@ ospf_lsa_update_handler_rx(ospf_interface_s *ospf_interface,
if(ospf_interface->state == OSPF_IFSTATE_BACKUP &&
ospf_interface->dr == pdu->router_id) {
/* Send delayed LSA ack. */
ospf_lsa_tree_add(lsa, NULL, ospf_interface->lsa_ack_tree[lsa->type]);
ospf_lsa_tree_add(lsa, NULL, ospf_interface->lsa_ack_tree[lsa_type]);
}
} else {
/* Send direct LSA ack. */
ospf_lsa_tree_add(lsa, NULL, ospf_neighbor->lsa_ack_tree[lsa->type]);
ospf_lsa_tree_add(lsa, NULL, ospf_neighbor->lsa_ack_tree[lsa_type]);
}
/* Next LSA from update ... */
continue;
}
/* RECEIVED LSA IS NEWER ... */
if(lsa->source.type == OSPF_SOURCE_EXTERNAL) {
LOG(OSPF, "OSPF RX TYPE-%u-LSA %s (seq %u router %s) overwrite external LSA with seq %u\n",
lsa_type, format_ipv4_address(&lsa_id), be32toh(hdr->seq),
format_ipv4_address(&lsa_router), lsa->seq);
} else if(lsa->source.type == OSPF_SOURCE_SELF) {
lsa->seq = be32toh(hdr->seq);
ospf_lsa_self_update(ospf_instance);
continue;
}
} else {
/* NEW LSA */
lsa = ospf_lsa_new(hdr->type, key, ospf_instance);
result = hb_tree_insert(ospf_instance->lsdb[hdr->type], &lsa->key);
lsa = ospf_lsa_new(lsa_type, key, ospf_instance);
result = hb_tree_insert(ospf_instance->lsdb[lsa_type], &lsa->key);
assert(result.inserted);
if(result.inserted) {
*result.datum_ptr = lsa;
@@ -1019,10 +1085,21 @@ ospf_lsa_update_handler_rx(ospf_interface_s *ospf_interface,
ospf_lsa_flood(lsa);
ospf_lsa_tree_add(lsa, NULL, ospf_interface->lsa_ack_tree[lsa->type]);
}
/* Send direct LSA ack. */
ospf_lsa_ack_tx(ospf_interface, ospf_neighbor);
/* Send direct LSA update. */
ospf_lsa_update_tx(ospf_interface, ospf_neighbor, false);
/* Set neighbor state to FULL if request tree becomes empty. */
if(ospf_neighbor->state == OSPF_NBSTATE_LOADING) {
lsa_count = 0;
for(lsa_type=OSPF_LSA_TYPE_1; lsa_type < OSPF_LSA_TYPE_MAX; lsa_type++) {
lsa_count += hb_tree_count(ospf_neighbor->lsa_request_tree[lsa_type]);
}
if(lsa_count == 0) {
ospf_neigbor_state(ospf_neighbor, OSPF_NBSTATE_FULL);
}
}
}
/**