3280 lines
92 KiB
C
3280 lines
92 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
/*
|
||
|
* Copyright (C) 2003 Yasuhiro Ohara
|
||
|
*/
|
||
|
|
||
|
#include <zebra.h>
|
||
|
|
||
|
#include "memory.h"
|
||
|
#include "log.h"
|
||
|
#include "vty.h"
|
||
|
#include "command.h"
|
||
|
#include "frrevent.h"
|
||
|
#include "linklist.h"
|
||
|
#include "lib_errors.h"
|
||
|
#include "checksum.h"
|
||
|
#include "network.h"
|
||
|
|
||
|
#include "ospf6_proto.h"
|
||
|
#include "ospf6_lsa.h"
|
||
|
#include "ospf6_lsdb.h"
|
||
|
#include "ospf6_top.h"
|
||
|
#include "ospf6_network.h"
|
||
|
#include "ospf6_message.h"
|
||
|
|
||
|
#include "ospf6_area.h"
|
||
|
#include "ospf6_neighbor.h"
|
||
|
#include "ospf6_interface.h"
|
||
|
|
||
|
/* for structures and macros ospf6_lsa_examin() needs */
|
||
|
#include "ospf6_abr.h"
|
||
|
#include "ospf6_asbr.h"
|
||
|
#include "ospf6_intra.h"
|
||
|
|
||
|
#include "ospf6_flood.h"
|
||
|
#include "ospf6d.h"
|
||
|
#include "ospf6_gr.h"
|
||
|
#include <netinet/ip6.h>
|
||
|
#include "lib/libospf.h"
|
||
|
#include "lib/keychain.h"
|
||
|
#include "ospf6_auth_trailer.h"
|
||
|
|
||
|
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_MESSAGE, "OSPF6 message");
|
||
|
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PACKET, "OSPF6 packet");
|
||
|
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_FIFO, "OSPF6 FIFO queue");
|
||
|
|
||
|
unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0};
|
||
|
|
||
|
const char *ospf6_message_type(int type)
|
||
|
{
|
||
|
switch (type) {
|
||
|
case OSPF6_MESSAGE_TYPE_HELLO:
|
||
|
return "Hello";
|
||
|
case OSPF6_MESSAGE_TYPE_DBDESC:
|
||
|
return "DbDesc";
|
||
|
case OSPF6_MESSAGE_TYPE_LSREQ:
|
||
|
return "LSReq";
|
||
|
case OSPF6_MESSAGE_TYPE_LSUPDATE:
|
||
|
return "LSUpdate";
|
||
|
case OSPF6_MESSAGE_TYPE_LSACK:
|
||
|
return "LSAck";
|
||
|
case OSPF6_MESSAGE_TYPE_UNKNOWN:
|
||
|
default:
|
||
|
return "unknown";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Minimum (besides the standard OSPF packet header) lengths for OSPF
|
||
|
packets of particular types, offset is the "type" field. */
|
||
|
const uint16_t ospf6_packet_minlen[OSPF6_MESSAGE_TYPE_ALL] = {
|
||
|
0,
|
||
|
OSPF6_HELLO_MIN_SIZE,
|
||
|
OSPF6_DB_DESC_MIN_SIZE,
|
||
|
OSPF6_LS_REQ_MIN_SIZE,
|
||
|
OSPF6_LS_UPD_MIN_SIZE,
|
||
|
OSPF6_LS_ACK_MIN_SIZE};
|
||
|
|
||
|
/* Minimum (besides the standard LSA header) lengths for LSAs of particular
|
||
|
types, offset is the "LSA function code" portion of "LSA type" field. */
|
||
|
const uint16_t ospf6_lsa_minlen[OSPF6_LSTYPE_SIZE] = {
|
||
|
0,
|
||
|
/* 0x2001 */ OSPF6_ROUTER_LSA_MIN_SIZE,
|
||
|
/* 0x2002 */ OSPF6_NETWORK_LSA_MIN_SIZE,
|
||
|
/* 0x2003 */ OSPF6_INTER_PREFIX_LSA_MIN_SIZE,
|
||
|
/* 0x2004 */ OSPF6_INTER_ROUTER_LSA_FIX_SIZE,
|
||
|
/* 0x4005 */ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE,
|
||
|
/* 0x2006 */ 0,
|
||
|
/* 0x2007 */ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE,
|
||
|
/* 0x0008 */ OSPF6_LINK_LSA_MIN_SIZE,
|
||
|
/* 0x2009 */ OSPF6_INTRA_PREFIX_LSA_MIN_SIZE,
|
||
|
/* 0x200a */ 0,
|
||
|
/* 0x000b */ OSPF6_GRACE_LSA_MIN_SIZE};
|
||
|
|
||
|
/* print functions */
|
||
|
|
||
|
static void ospf6_header_print(struct ospf6_header *oh)
|
||
|
{
|
||
|
zlog_debug(" OSPFv%d Type:%d Len:%hu Router-ID:%pI4", oh->version,
|
||
|
oh->type, ntohs(oh->length), &oh->router_id);
|
||
|
zlog_debug(" Area-ID:%pI4 Cksum:%hx Instance-ID:%d", &oh->area_id,
|
||
|
ntohs(oh->checksum), oh->instance_id);
|
||
|
}
|
||
|
|
||
|
void ospf6_hello_print(struct ospf6_header *oh, int action)
|
||
|
{
|
||
|
struct ospf6_hello *hello;
|
||
|
char options[32];
|
||
|
char *p;
|
||
|
|
||
|
ospf6_header_print(oh);
|
||
|
assert(oh->type == OSPF6_MESSAGE_TYPE_HELLO);
|
||
|
|
||
|
hello = (struct ospf6_hello *)((caddr_t)oh
|
||
|
+ sizeof(struct ospf6_header));
|
||
|
|
||
|
ospf6_options_printbuf(hello->options, options, sizeof(options));
|
||
|
|
||
|
zlog_debug(" I/F-Id:%ld Priority:%d Option:%s",
|
||
|
(unsigned long)ntohl(hello->interface_id), hello->priority,
|
||
|
options);
|
||
|
zlog_debug(" HelloInterval:%hu DeadInterval:%hu",
|
||
|
ntohs(hello->hello_interval), ntohs(hello->dead_interval));
|
||
|
zlog_debug(" DR:%pI4 BDR:%pI4", &hello->drouter, &hello->bdrouter);
|
||
|
|
||
|
if ((IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)
|
||
|
&& action == OSPF6_ACTION_RECV)
|
||
|
|| (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)
|
||
|
&& action == OSPF6_ACTION_SEND)) {
|
||
|
|
||
|
for (p = (char *)((caddr_t)hello + sizeof(struct ospf6_hello));
|
||
|
p + sizeof(uint32_t) <= OSPF6_MESSAGE_END(oh);
|
||
|
p += sizeof(uint32_t))
|
||
|
zlog_debug(" Neighbor: %pI4", (in_addr_t *)p);
|
||
|
|
||
|
assert(p == OSPF6_MESSAGE_END(oh));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ospf6_dbdesc_print(struct ospf6_header *oh, int action)
|
||
|
{
|
||
|
struct ospf6_dbdesc *dbdesc;
|
||
|
char options[32];
|
||
|
char *p;
|
||
|
|
||
|
ospf6_header_print(oh);
|
||
|
assert(oh->type == OSPF6_MESSAGE_TYPE_DBDESC);
|
||
|
|
||
|
dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh
|
||
|
+ sizeof(struct ospf6_header));
|
||
|
|
||
|
ospf6_options_printbuf(dbdesc->options, options, sizeof(options));
|
||
|
|
||
|
zlog_debug(" MBZ: %#x Option: %s IfMTU: %hu", dbdesc->reserved1,
|
||
|
options, ntohs(dbdesc->ifmtu));
|
||
|
zlog_debug(" MBZ: %#x Bits: %s%s%s SeqNum: %#lx", dbdesc->reserved2,
|
||
|
(CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT) ? "I" : "-"),
|
||
|
(CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MBIT) ? "M" : "-"),
|
||
|
(CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT) ? "m" : "s"),
|
||
|
(unsigned long)ntohl(dbdesc->seqnum));
|
||
|
|
||
|
if ((IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)
|
||
|
&& action == OSPF6_ACTION_RECV)
|
||
|
|| (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)
|
||
|
&& action == OSPF6_ACTION_SEND)) {
|
||
|
|
||
|
for (p = (char *)((caddr_t)dbdesc
|
||
|
+ sizeof(struct ospf6_dbdesc));
|
||
|
p + sizeof(struct ospf6_lsa_header)
|
||
|
<= OSPF6_MESSAGE_END(oh);
|
||
|
p += sizeof(struct ospf6_lsa_header))
|
||
|
ospf6_lsa_header_print_raw(
|
||
|
(struct ospf6_lsa_header *)p);
|
||
|
|
||
|
assert(p == OSPF6_MESSAGE_END(oh));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ospf6_lsreq_print(struct ospf6_header *oh, int action)
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
ospf6_header_print(oh);
|
||
|
assert(oh->type == OSPF6_MESSAGE_TYPE_LSREQ);
|
||
|
|
||
|
if ((IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)
|
||
|
&& action == OSPF6_ACTION_RECV)
|
||
|
|| (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)
|
||
|
&& action == OSPF6_ACTION_SEND)) {
|
||
|
|
||
|
for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header));
|
||
|
p + sizeof(struct ospf6_lsreq_entry)
|
||
|
<= OSPF6_MESSAGE_END(oh);
|
||
|
p += sizeof(struct ospf6_lsreq_entry)) {
|
||
|
struct ospf6_lsreq_entry *e =
|
||
|
(struct ospf6_lsreq_entry *)p;
|
||
|
|
||
|
zlog_debug(" [%s Id:%pI4 Adv:%pI4]",
|
||
|
ospf6_lstype_name(e->type), &e->id,
|
||
|
&e->adv_router);
|
||
|
}
|
||
|
|
||
|
assert(p == OSPF6_MESSAGE_END(oh));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ospf6_lsupdate_print(struct ospf6_header *oh, int action)
|
||
|
{
|
||
|
struct ospf6_lsupdate *lsupdate;
|
||
|
unsigned long num;
|
||
|
char *p;
|
||
|
|
||
|
ospf6_header_print(oh);
|
||
|
assert(oh->type == OSPF6_MESSAGE_TYPE_LSUPDATE);
|
||
|
|
||
|
lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
|
||
|
+ sizeof(struct ospf6_header));
|
||
|
|
||
|
num = ntohl(lsupdate->lsa_number);
|
||
|
zlog_debug(" Number of LSA: %ld", num);
|
||
|
|
||
|
if ((IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)
|
||
|
&& action == OSPF6_ACTION_RECV)
|
||
|
|| (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)
|
||
|
&& action == OSPF6_ACTION_SEND)) {
|
||
|
for (p = (char *)((caddr_t)lsupdate +
|
||
|
sizeof(struct ospf6_lsupdate));
|
||
|
p < OSPF6_MESSAGE_END(oh) &&
|
||
|
p + ospf6_lsa_size((struct ospf6_lsa_header *)p) <=
|
||
|
OSPF6_MESSAGE_END(oh);
|
||
|
p += ospf6_lsa_size((struct ospf6_lsa_header *)p)) {
|
||
|
ospf6_lsa_header_print_raw(
|
||
|
(struct ospf6_lsa_header *)p);
|
||
|
}
|
||
|
|
||
|
assert(p == OSPF6_MESSAGE_END(oh));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ospf6_lsack_print(struct ospf6_header *oh, int action)
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
ospf6_header_print(oh);
|
||
|
assert(oh->type == OSPF6_MESSAGE_TYPE_LSACK);
|
||
|
|
||
|
if ((IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)
|
||
|
&& action == OSPF6_ACTION_RECV)
|
||
|
|| (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)
|
||
|
&& action == OSPF6_ACTION_SEND)) {
|
||
|
|
||
|
for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header));
|
||
|
p + sizeof(struct ospf6_lsa_header)
|
||
|
<= OSPF6_MESSAGE_END(oh);
|
||
|
p += sizeof(struct ospf6_lsa_header))
|
||
|
ospf6_lsa_header_print_raw(
|
||
|
(struct ospf6_lsa_header *)p);
|
||
|
|
||
|
assert(p == OSPF6_MESSAGE_END(oh));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static struct ospf6_packet *ospf6_packet_new(size_t size)
|
||
|
{
|
||
|
struct ospf6_packet *new;
|
||
|
|
||
|
new = XCALLOC(MTYPE_OSPF6_PACKET, sizeof(struct ospf6_packet));
|
||
|
new->s = stream_new(size);
|
||
|
|
||
|
return new;
|
||
|
}
|
||
|
|
||
|
static struct ospf6_packet *ospf6_packet_dup(struct ospf6_packet *old)
|
||
|
{
|
||
|
struct ospf6_packet *new;
|
||
|
|
||
|
new = XCALLOC(MTYPE_OSPF6_PACKET, sizeof(struct ospf6_packet));
|
||
|
new->s = stream_dup(old->s);
|
||
|
new->dst = old->dst;
|
||
|
new->length = old->length;
|
||
|
|
||
|
return new;
|
||
|
}
|
||
|
|
||
|
static void ospf6_packet_free(struct ospf6_packet *op)
|
||
|
{
|
||
|
if (op->s)
|
||
|
stream_free(op->s);
|
||
|
|
||
|
XFREE(MTYPE_OSPF6_PACKET, op);
|
||
|
}
|
||
|
|
||
|
struct ospf6_fifo *ospf6_fifo_new(void)
|
||
|
{
|
||
|
struct ospf6_fifo *new;
|
||
|
|
||
|
new = XCALLOC(MTYPE_OSPF6_FIFO, sizeof(struct ospf6_fifo));
|
||
|
return new;
|
||
|
}
|
||
|
|
||
|
/* Add new packet to fifo. */
|
||
|
static void ospf6_fifo_push(struct ospf6_fifo *fifo, struct ospf6_packet *op)
|
||
|
{
|
||
|
if (fifo->tail)
|
||
|
fifo->tail->next = op;
|
||
|
else
|
||
|
fifo->head = op;
|
||
|
|
||
|
fifo->tail = op;
|
||
|
|
||
|
fifo->count++;
|
||
|
}
|
||
|
|
||
|
/* Add new packet to head of fifo. */
|
||
|
static void ospf6_fifo_push_head(struct ospf6_fifo *fifo,
|
||
|
struct ospf6_packet *op)
|
||
|
{
|
||
|
op->next = fifo->head;
|
||
|
|
||
|
if (fifo->tail == NULL)
|
||
|
fifo->tail = op;
|
||
|
|
||
|
fifo->head = op;
|
||
|
|
||
|
fifo->count++;
|
||
|
}
|
||
|
|
||
|
/* Delete first packet from fifo. */
|
||
|
static struct ospf6_packet *ospf6_fifo_pop(struct ospf6_fifo *fifo)
|
||
|
{
|
||
|
struct ospf6_packet *op;
|
||
|
|
||
|
op = fifo->head;
|
||
|
|
||
|
if (op) {
|
||
|
fifo->head = op->next;
|
||
|
|
||
|
if (fifo->head == NULL)
|
||
|
fifo->tail = NULL;
|
||
|
|
||
|
fifo->count--;
|
||
|
}
|
||
|
|
||
|
return op;
|
||
|
}
|
||
|
|
||
|
/* Return first fifo entry. */
|
||
|
static struct ospf6_packet *ospf6_fifo_head(struct ospf6_fifo *fifo)
|
||
|
{
|
||
|
return fifo->head;
|
||
|
}
|
||
|
|
||
|
/* Flush ospf packet fifo. */
|
||
|
void ospf6_fifo_flush(struct ospf6_fifo *fifo)
|
||
|
{
|
||
|
struct ospf6_packet *op;
|
||
|
struct ospf6_packet *next;
|
||
|
|
||
|
for (op = fifo->head; op; op = next) {
|
||
|
next = op->next;
|
||
|
ospf6_packet_free(op);
|
||
|
}
|
||
|
fifo->head = fifo->tail = NULL;
|
||
|
fifo->count = 0;
|
||
|
}
|
||
|
|
||
|
/* Free ospf packet fifo. */
|
||
|
void ospf6_fifo_free(struct ospf6_fifo *fifo)
|
||
|
{
|
||
|
ospf6_fifo_flush(fifo);
|
||
|
|
||
|
XFREE(MTYPE_OSPF6_FIFO, fifo);
|
||
|
}
|
||
|
|
||
|
static void ospf6_packet_add(struct ospf6_interface *oi,
|
||
|
struct ospf6_packet *op)
|
||
|
{
|
||
|
/* Add packet to end of queue. */
|
||
|
ospf6_fifo_push(oi->obuf, op);
|
||
|
|
||
|
/* Debug of packet fifo*/
|
||
|
/* ospf_fifo_debug (oi->obuf); */
|
||
|
}
|
||
|
|
||
|
static void ospf6_packet_add_top(struct ospf6_interface *oi,
|
||
|
struct ospf6_packet *op)
|
||
|
{
|
||
|
/* Add packet to head of queue. */
|
||
|
ospf6_fifo_push_head(oi->obuf, op);
|
||
|
|
||
|
/* Debug of packet fifo*/
|
||
|
/* ospf_fifo_debug (oi->obuf); */
|
||
|
}
|
||
|
|
||
|
static void ospf6_packet_delete(struct ospf6_interface *oi)
|
||
|
{
|
||
|
struct ospf6_packet *op;
|
||
|
|
||
|
op = ospf6_fifo_pop(oi->obuf);
|
||
|
|
||
|
if (op)
|
||
|
ospf6_packet_free(op);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst,
|
||
|
struct ospf6_interface *oi,
|
||
|
struct ospf6_header *oh)
|
||
|
{
|
||
|
struct ospf6_hello *hello;
|
||
|
struct ospf6_neighbor *on;
|
||
|
char *p;
|
||
|
int twoway = 0;
|
||
|
int neighborchange = 0;
|
||
|
int neighbor_ifindex_change = 0;
|
||
|
int backupseen = 0;
|
||
|
int64_t latency = 0;
|
||
|
struct timeval timestamp;
|
||
|
|
||
|
monotime(×tamp);
|
||
|
hello = (struct ospf6_hello *)((caddr_t)oh
|
||
|
+ sizeof(struct ospf6_header));
|
||
|
|
||
|
if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT
|
||
|
|| oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT)
|
||
|
&& oi->p2xp_only_cfg_neigh) {
|
||
|
/* NEVER, never, ever, do this on broadcast (or NBMA)!
|
||
|
* DR/BDR election requires everyone to talk to everyone else
|
||
|
* only for PtP/PtMP we can be selective in adjacencies!
|
||
|
*/
|
||
|
struct ospf6_if_p2xp_neighcfg *p2xp_cfg;
|
||
|
|
||
|
p2xp_cfg = ospf6_if_p2xp_find(oi, src);
|
||
|
if (!p2xp_cfg) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug(
|
||
|
"ignoring PtP/PtMP hello from %pI6, neighbor not configured",
|
||
|
src);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* HelloInterval check */
|
||
|
if (ntohs(hello->hello_interval) != oi->hello_interval) {
|
||
|
zlog_warn(
|
||
|
"VRF %s: I/F %s HelloInterval mismatch from %pI6 (%pI4): (my %d, rcvd %d)",
|
||
|
oi->interface->vrf->name, oi->interface->name, src,
|
||
|
&oh->router_id, oi->hello_interval,
|
||
|
ntohs(hello->hello_interval));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* RouterDeadInterval check */
|
||
|
if (ntohs(hello->dead_interval) != oi->dead_interval) {
|
||
|
zlog_warn(
|
||
|
"VRF %s: I/F %s DeadInterval mismatch from %pI6 (%pI4): (my %d, rcvd %d)",
|
||
|
oi->interface->vrf->name, oi->interface->name, src,
|
||
|
&oh->router_id, oi->dead_interval,
|
||
|
ntohs(hello->dead_interval));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* E-bit check */
|
||
|
if (OSPF6_OPT_ISSET(hello->options, OSPF6_OPT_E) !=
|
||
|
OSPF6_OPT_ISSET(oi->area->options, OSPF6_OPT_E)) {
|
||
|
zlog_warn("VRF %s: IF %s E-bit mismatch from %pI6 (%pI4)",
|
||
|
oi->interface->vrf->name, oi->interface->name, src,
|
||
|
&oh->router_id);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* N-bit check */
|
||
|
if (OSPF6_OPT_ISSET(hello->options, OSPF6_OPT_N)
|
||
|
!= OSPF6_OPT_ISSET(oi->area->options, OSPF6_OPT_N)) {
|
||
|
zlog_warn("VRF %s: IF %s N-bit mismatch",
|
||
|
oi->interface->vrf->name, oi->interface->name);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (((OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_AT) ==
|
||
|
OSPF6_OPT_AT) &&
|
||
|
(oi->at_data.flags == 0)) ||
|
||
|
((OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_AT) !=
|
||
|
OSPF6_OPT_AT) &&
|
||
|
(oi->at_data.flags != 0))) {
|
||
|
if (IS_OSPF6_DEBUG_AUTH_RX)
|
||
|
zlog_warn(
|
||
|
"VRF %s: IF %s AT-bit mismatch in hello packet",
|
||
|
oi->interface->vrf->name, oi->interface->name);
|
||
|
oi->at_data.rx_drop++;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Find neighbor, create if not exist */
|
||
|
on = ospf6_neighbor_lookup(oh->router_id, oi);
|
||
|
if (on == NULL) {
|
||
|
on = ospf6_neighbor_create(oh->router_id, oi);
|
||
|
on->prev_drouter = on->drouter = hello->drouter;
|
||
|
on->prev_bdrouter = on->bdrouter = hello->bdrouter;
|
||
|
on->priority = hello->priority;
|
||
|
}
|
||
|
|
||
|
/* check latency against hello period */
|
||
|
if (on->hello_in)
|
||
|
latency = monotime_since(&on->last_hello, NULL)
|
||
|
- ((int64_t)oi->hello_interval * 1000000);
|
||
|
/* log if latency exceeds the hello period */
|
||
|
if (latency > ((int64_t)oi->hello_interval * 1000000))
|
||
|
zlog_warn("%s RX %pI4 high latency %" PRId64 "us.", __func__,
|
||
|
&on->router_id, latency);
|
||
|
on->last_hello = timestamp;
|
||
|
on->hello_in++;
|
||
|
|
||
|
/* Always override neighbor's source address */
|
||
|
ospf6_neighbor_lladdr_set(on, src);
|
||
|
|
||
|
/* Neighbor ifindex check */
|
||
|
if (on->ifindex != (ifindex_t)ntohl(hello->interface_id)) {
|
||
|
on->ifindex = ntohl(hello->interface_id);
|
||
|
neighbor_ifindex_change++;
|
||
|
}
|
||
|
|
||
|
/* TwoWay check */
|
||
|
for (p = (char *)((caddr_t)hello + sizeof(struct ospf6_hello));
|
||
|
p + sizeof(uint32_t) <= OSPF6_MESSAGE_END(oh);
|
||
|
p += sizeof(uint32_t)) {
|
||
|
uint32_t *router_id = (uint32_t *)p;
|
||
|
|
||
|
if (*router_id == oi->area->ospf6->router_id)
|
||
|
twoway++;
|
||
|
}
|
||
|
|
||
|
assert(p == OSPF6_MESSAGE_END(oh));
|
||
|
|
||
|
/* RouterPriority check */
|
||
|
if (on->priority != hello->priority) {
|
||
|
on->priority = hello->priority;
|
||
|
neighborchange++;
|
||
|
}
|
||
|
|
||
|
/* DR check */
|
||
|
if (on->drouter != hello->drouter) {
|
||
|
on->prev_drouter = on->drouter;
|
||
|
on->drouter = hello->drouter;
|
||
|
if (on->prev_drouter == on->router_id
|
||
|
|| on->drouter == on->router_id)
|
||
|
neighborchange++;
|
||
|
}
|
||
|
|
||
|
/* BDR check */
|
||
|
if (on->bdrouter != hello->bdrouter) {
|
||
|
on->prev_bdrouter = on->bdrouter;
|
||
|
on->bdrouter = hello->bdrouter;
|
||
|
if (on->prev_bdrouter == on->router_id
|
||
|
|| on->bdrouter == on->router_id)
|
||
|
neighborchange++;
|
||
|
}
|
||
|
|
||
|
/* BackupSeen check */
|
||
|
if (oi->state == OSPF6_INTERFACE_WAITING) {
|
||
|
if (hello->bdrouter == on->router_id)
|
||
|
backupseen++;
|
||
|
else if (hello->drouter == on->router_id
|
||
|
&& hello->bdrouter == htonl(0))
|
||
|
backupseen++;
|
||
|
}
|
||
|
|
||
|
oi->hello_in++;
|
||
|
|
||
|
/* Execute neighbor events */
|
||
|
event_execute(master, hello_received, on, 0, NULL);
|
||
|
if (twoway)
|
||
|
event_execute(master, twoway_received, on, 0, NULL);
|
||
|
else {
|
||
|
if (OSPF6_GR_IS_ACTIVE_HELPER(on)) {
|
||
|
if (IS_DEBUG_OSPF6_GR)
|
||
|
zlog_debug(
|
||
|
"%s, Received oneway hello from RESTARTER so ignore here.",
|
||
|
__PRETTY_FUNCTION__);
|
||
|
} else {
|
||
|
/* If the router is DR_OTHER, RESTARTER will not wait
|
||
|
* until it receives the hello from it if it receives
|
||
|
* from DR and BDR.
|
||
|
* So, helper might receives ONE_WAY hello from
|
||
|
* RESTARTER. So not allowing to change the state if it
|
||
|
* receives one_way hellow when it acts as HELPER for
|
||
|
* that specific neighbor.
|
||
|
*/
|
||
|
event_execute(master, oneway_received, on, 0, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (OSPF6_GR_IS_ACTIVE_HELPER(on)) {
|
||
|
/* As per the GR Conformance Test Case 7.2. Section 3
|
||
|
* "Also, if X was the Designated Router on network segment S
|
||
|
* when the helping relationship began, Y maintains X as the
|
||
|
* Designated Router until the helping relationship is
|
||
|
* terminated."
|
||
|
* When it is a helper for this neighbor, It should not trigger
|
||
|
* the ISM Events. Also Intentionally not setting the priority
|
||
|
* and other fields so that when the neighbor exits the Grace
|
||
|
* period, it can handle if there is any change before GR and
|
||
|
* after GR.
|
||
|
*/
|
||
|
if (IS_DEBUG_OSPF6_GR)
|
||
|
zlog_debug(
|
||
|
"%s, Neighbor is under GR Restart, hence ignoring the ISM Events",
|
||
|
__PRETTY_FUNCTION__);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* RFC 3623 - Section 2:
|
||
|
* "If the restarting router determines that it was the Designated
|
||
|
* Router on a given segment prior to the restart, it elects
|
||
|
* itself as the Designated Router again. The restarting router
|
||
|
* knows that it was the Designated Router if, while the
|
||
|
* associated interface is in Waiting state, a Hello packet is
|
||
|
* received from a neighbor listing the router as the Designated
|
||
|
* Router".
|
||
|
*/
|
||
|
if (oi->area->ospf6->gr_info.restart_in_progress
|
||
|
&& oi->state == OSPF6_INTERFACE_WAITING
|
||
|
&& hello->drouter == oi->area->ospf6->router_id)
|
||
|
oi->drouter = hello->drouter;
|
||
|
|
||
|
/* Schedule interface events */
|
||
|
if (backupseen)
|
||
|
event_add_event(master, backup_seen, oi, 0, NULL);
|
||
|
if (neighborchange)
|
||
|
event_add_event(master, neighbor_change, oi, 0, NULL);
|
||
|
|
||
|
if (neighbor_ifindex_change && on->state == OSPF6_NEIGHBOR_FULL)
|
||
|
OSPF6_ROUTER_LSA_SCHEDULE(oi->area);
|
||
|
}
|
||
|
|
||
|
static void ospf6_dbdesc_recv_master(struct ospf6_header *oh,
|
||
|
struct ospf6_neighbor *on)
|
||
|
{
|
||
|
struct ospf6_dbdesc *dbdesc;
|
||
|
char *p;
|
||
|
|
||
|
dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh
|
||
|
+ sizeof(struct ospf6_header));
|
||
|
|
||
|
if (on->state < OSPF6_NEIGHBOR_INIT) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Neighbor state less than Init, ignore");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (on->state) {
|
||
|
case OSPF6_NEIGHBOR_TWOWAY:
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Neighbor state is 2-Way, ignore");
|
||
|
return;
|
||
|
|
||
|
case OSPF6_NEIGHBOR_INIT:
|
||
|
event_execute(master, twoway_received, on, 0, NULL);
|
||
|
if (on->state != OSPF6_NEIGHBOR_EXSTART) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug(
|
||
|
"Neighbor state is not ExStart, ignore");
|
||
|
return;
|
||
|
}
|
||
|
/* else fall through to ExStart */
|
||
|
fallthrough;
|
||
|
case OSPF6_NEIGHBOR_EXSTART:
|
||
|
/* if neighbor obeys us as our slave, schedule negotiation_done
|
||
|
and process LSA Headers. Otherwise, ignore this message */
|
||
|
if (!CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)
|
||
|
&& !CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)
|
||
|
&& ntohl(dbdesc->seqnum) == on->dbdesc_seqnum) {
|
||
|
/* execute NegotiationDone */
|
||
|
event_execute(master, negotiation_done, on, 0, NULL);
|
||
|
|
||
|
/* Record neighbor options */
|
||
|
memcpy(on->options, dbdesc->options,
|
||
|
sizeof(on->options));
|
||
|
} else {
|
||
|
zlog_warn("VRF %s: Nbr %s: Negotiation failed",
|
||
|
on->ospf6_if->interface->vrf->name, on->name);
|
||
|
return;
|
||
|
}
|
||
|
/* fall through to exchange */
|
||
|
fallthrough;
|
||
|
case OSPF6_NEIGHBOR_EXCHANGE:
|
||
|
if (!memcmp(dbdesc, &on->dbdesc_last,
|
||
|
sizeof(struct ospf6_dbdesc))) {
|
||
|
/* Duplicated DatabaseDescription is dropped by master
|
||
|
*/
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug(
|
||
|
"Duplicated dbdesc discarded by Master, ignore");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)) {
|
||
|
zlog_warn(
|
||
|
"DbDesc recv: Master/Slave bit mismatch Nbr %s",
|
||
|
on->name);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0,
|
||
|
NULL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)) {
|
||
|
zlog_warn("DbDesc recv: Initialize bit mismatch Nbr %s",
|
||
|
on->name);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0,
|
||
|
NULL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (memcmp(on->options, dbdesc->options, sizeof(on->options))) {
|
||
|
zlog_warn("DbDesc recv: Option field mismatch Nbr %s",
|
||
|
on->name);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0,
|
||
|
NULL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum) {
|
||
|
zlog_warn(
|
||
|
"DbDesc recv: Sequence number mismatch Nbr %s (received %#lx, %#lx expected)",
|
||
|
on->name, (unsigned long)ntohl(dbdesc->seqnum),
|
||
|
(unsigned long)on->dbdesc_seqnum);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0,
|
||
|
NULL);
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case OSPF6_NEIGHBOR_LOADING:
|
||
|
case OSPF6_NEIGHBOR_FULL:
|
||
|
if (!memcmp(dbdesc, &on->dbdesc_last,
|
||
|
sizeof(struct ospf6_dbdesc))) {
|
||
|
/* Duplicated DatabaseDescription is dropped by master
|
||
|
*/
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug(
|
||
|
"Duplicated dbdesc discarded by Master, ignore");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
zlog_warn(
|
||
|
"DbDesc recv: Not duplicate dbdesc in state %s Nbr %s",
|
||
|
ospf6_neighbor_state_str[on->state], on->name);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0, NULL);
|
||
|
return;
|
||
|
|
||
|
default:
|
||
|
assert(0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Process LSA headers */
|
||
|
for (p = (char *)((caddr_t)dbdesc + sizeof(struct ospf6_dbdesc));
|
||
|
p + sizeof(struct ospf6_lsa_header) <= OSPF6_MESSAGE_END(oh);
|
||
|
p += sizeof(struct ospf6_lsa_header)) {
|
||
|
struct ospf6_lsa *his, *mine;
|
||
|
struct ospf6_lsdb *lsdb = NULL;
|
||
|
|
||
|
his = ospf6_lsa_create_headeronly((struct ospf6_lsa_header *)p);
|
||
|
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("%s", his->name);
|
||
|
|
||
|
switch (OSPF6_LSA_SCOPE(his->header->type)) {
|
||
|
case OSPF6_SCOPE_LINKLOCAL:
|
||
|
lsdb = on->ospf6_if->lsdb;
|
||
|
break;
|
||
|
case OSPF6_SCOPE_AREA:
|
||
|
lsdb = on->ospf6_if->area->lsdb;
|
||
|
break;
|
||
|
case OSPF6_SCOPE_AS:
|
||
|
lsdb = on->ospf6_if->area->ospf6->lsdb;
|
||
|
break;
|
||
|
case OSPF6_SCOPE_RESERVED:
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("Ignoring LSA of reserved scope");
|
||
|
ospf6_lsa_delete(his);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (ntohs(his->header->type) == OSPF6_LSTYPE_AS_EXTERNAL
|
||
|
&& (IS_AREA_STUB(on->ospf6_if->area)
|
||
|
|| IS_AREA_NSSA(on->ospf6_if->area))) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug(
|
||
|
"SeqNumMismatch (E-bit mismatch), discard");
|
||
|
ospf6_lsa_delete(his);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0,
|
||
|
NULL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
mine = ospf6_lsdb_lookup(his->header->type, his->header->id,
|
||
|
his->header->adv_router, lsdb);
|
||
|
if (mine == NULL) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("Add request (No database copy)");
|
||
|
ospf6_lsdb_add(ospf6_lsa_copy(his), on->request_list);
|
||
|
} else if (ospf6_lsa_compare(his, mine) < 0) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("Add request (Received MoreRecent)");
|
||
|
ospf6_lsdb_add(ospf6_lsa_copy(his), on->request_list);
|
||
|
} else {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("Discard (Existing MoreRecent)");
|
||
|
}
|
||
|
ospf6_lsa_delete(his);
|
||
|
}
|
||
|
|
||
|
assert(p == OSPF6_MESSAGE_END(oh));
|
||
|
|
||
|
/* Increment sequence number */
|
||
|
on->dbdesc_seqnum++;
|
||
|
|
||
|
/* schedule send lsreq */
|
||
|
if (on->request_list->count)
|
||
|
event_add_event(master, ospf6_lsreq_send, on, 0,
|
||
|
&on->thread_send_lsreq);
|
||
|
|
||
|
EVENT_OFF(on->thread_send_dbdesc);
|
||
|
|
||
|
/* More bit check */
|
||
|
if (!CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MBIT)
|
||
|
&& !CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT))
|
||
|
event_add_event(master, exchange_done, on, 0,
|
||
|
&on->thread_exchange_done);
|
||
|
else {
|
||
|
event_add_event(master, ospf6_dbdesc_send_newone, on, 0,
|
||
|
&on->thread_send_dbdesc);
|
||
|
}
|
||
|
|
||
|
/* save last received dbdesc */
|
||
|
memcpy(&on->dbdesc_last, dbdesc, sizeof(struct ospf6_dbdesc));
|
||
|
}
|
||
|
|
||
|
static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh,
|
||
|
struct ospf6_neighbor *on)
|
||
|
{
|
||
|
struct ospf6_dbdesc *dbdesc;
|
||
|
char *p;
|
||
|
|
||
|
dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh
|
||
|
+ sizeof(struct ospf6_header));
|
||
|
|
||
|
if (on->state < OSPF6_NEIGHBOR_INIT) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Neighbor state less than Init, ignore");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (on->state) {
|
||
|
case OSPF6_NEIGHBOR_TWOWAY:
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Neighbor state is 2-Way, ignore");
|
||
|
return;
|
||
|
|
||
|
case OSPF6_NEIGHBOR_INIT:
|
||
|
event_execute(master, twoway_received, on, 0, NULL);
|
||
|
if (on->state != OSPF6_NEIGHBOR_EXSTART) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug(
|
||
|
"Neighbor state is not ExStart, ignore");
|
||
|
return;
|
||
|
}
|
||
|
/* else fall through to ExStart */
|
||
|
fallthrough;
|
||
|
case OSPF6_NEIGHBOR_EXSTART:
|
||
|
/* If the neighbor is Master, act as Slave. Schedule
|
||
|
negotiation_done
|
||
|
and process LSA Headers. Otherwise, ignore this message */
|
||
|
if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)
|
||
|
&& CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MBIT)
|
||
|
&& CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)
|
||
|
&& ntohs(oh->length)
|
||
|
== sizeof(struct ospf6_header)
|
||
|
+ sizeof(struct ospf6_dbdesc)) {
|
||
|
/* set the master/slave bit to slave */
|
||
|
UNSET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT);
|
||
|
|
||
|
/* set the DD sequence number to one specified by master
|
||
|
*/
|
||
|
on->dbdesc_seqnum = ntohl(dbdesc->seqnum);
|
||
|
|
||
|
/* schedule NegotiationDone */
|
||
|
event_execute(master, negotiation_done, on, 0, NULL);
|
||
|
|
||
|
/* Record neighbor options */
|
||
|
memcpy(on->options, dbdesc->options,
|
||
|
sizeof(on->options));
|
||
|
} else {
|
||
|
zlog_warn("VRF %s: Nbr %s Negotiation failed",
|
||
|
on->ospf6_if->interface->vrf->name, on->name);
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case OSPF6_NEIGHBOR_EXCHANGE:
|
||
|
if (!memcmp(dbdesc, &on->dbdesc_last,
|
||
|
sizeof(struct ospf6_dbdesc))) {
|
||
|
/* Duplicated DatabaseDescription causes slave to
|
||
|
* retransmit */
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug(
|
||
|
"Duplicated dbdesc causes retransmit");
|
||
|
EVENT_OFF(on->thread_send_dbdesc);
|
||
|
event_add_event(master, ospf6_dbdesc_send, on, 0,
|
||
|
&on->thread_send_dbdesc);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)) {
|
||
|
zlog_warn(
|
||
|
"DbDesc slave recv: Master/Slave bit mismatch Nbr %s",
|
||
|
on->name);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0,
|
||
|
NULL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)) {
|
||
|
zlog_warn(
|
||
|
"DbDesc slave recv: Initialize bit mismatch Nbr %s",
|
||
|
on->name);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0,
|
||
|
NULL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (memcmp(on->options, dbdesc->options, sizeof(on->options))) {
|
||
|
zlog_warn(
|
||
|
"DbDesc slave recv: Option field mismatch Nbr %s",
|
||
|
on->name);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0,
|
||
|
NULL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum + 1) {
|
||
|
zlog_warn(
|
||
|
"DbDesc slave recv: Sequence number mismatch Nbr %s (received %#lx, %#lx expected)",
|
||
|
on->name, (unsigned long)ntohl(dbdesc->seqnum),
|
||
|
(unsigned long)on->dbdesc_seqnum + 1);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0,
|
||
|
NULL);
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case OSPF6_NEIGHBOR_LOADING:
|
||
|
case OSPF6_NEIGHBOR_FULL:
|
||
|
if (!memcmp(dbdesc, &on->dbdesc_last,
|
||
|
sizeof(struct ospf6_dbdesc))) {
|
||
|
/* Duplicated DatabaseDescription causes slave to
|
||
|
* retransmit */
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug(
|
||
|
"Duplicated dbdesc causes retransmit");
|
||
|
EVENT_OFF(on->thread_send_dbdesc);
|
||
|
event_add_event(master, ospf6_dbdesc_send, on, 0,
|
||
|
&on->thread_send_dbdesc);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
zlog_warn(
|
||
|
"DbDesc slave recv: Not duplicate dbdesc in state %s Nbr %s",
|
||
|
ospf6_neighbor_state_str[on->state], on->name);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0, NULL);
|
||
|
return;
|
||
|
|
||
|
default:
|
||
|
assert(0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Process LSA headers */
|
||
|
for (p = (char *)((caddr_t)dbdesc + sizeof(struct ospf6_dbdesc));
|
||
|
p + sizeof(struct ospf6_lsa_header) <= OSPF6_MESSAGE_END(oh);
|
||
|
p += sizeof(struct ospf6_lsa_header)) {
|
||
|
struct ospf6_lsa *his, *mine;
|
||
|
struct ospf6_lsdb *lsdb = NULL;
|
||
|
|
||
|
his = ospf6_lsa_create_headeronly((struct ospf6_lsa_header *)p);
|
||
|
|
||
|
switch (OSPF6_LSA_SCOPE(his->header->type)) {
|
||
|
case OSPF6_SCOPE_LINKLOCAL:
|
||
|
lsdb = on->ospf6_if->lsdb;
|
||
|
break;
|
||
|
case OSPF6_SCOPE_AREA:
|
||
|
lsdb = on->ospf6_if->area->lsdb;
|
||
|
break;
|
||
|
case OSPF6_SCOPE_AS:
|
||
|
lsdb = on->ospf6_if->area->ospf6->lsdb;
|
||
|
break;
|
||
|
case OSPF6_SCOPE_RESERVED:
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("Ignoring LSA of reserved scope");
|
||
|
ospf6_lsa_delete(his);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (OSPF6_LSA_SCOPE(his->header->type) == OSPF6_SCOPE_AS
|
||
|
&& (IS_AREA_STUB(on->ospf6_if->area)
|
||
|
|| IS_AREA_NSSA(on->ospf6_if->area))) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("E-bit mismatch with LSA Headers");
|
||
|
ospf6_lsa_delete(his);
|
||
|
event_add_event(master, seqnumber_mismatch, on, 0,
|
||
|
NULL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
mine = ospf6_lsdb_lookup(his->header->type, his->header->id,
|
||
|
his->header->adv_router, lsdb);
|
||
|
if (mine == NULL || ospf6_lsa_compare(his, mine) < 0) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("Add request-list: %s", his->name);
|
||
|
ospf6_lsdb_add(ospf6_lsa_copy(his), on->request_list);
|
||
|
}
|
||
|
ospf6_lsa_delete(his);
|
||
|
}
|
||
|
|
||
|
assert(p == OSPF6_MESSAGE_END(oh));
|
||
|
|
||
|
/* Set sequence number to Master's */
|
||
|
on->dbdesc_seqnum = ntohl(dbdesc->seqnum);
|
||
|
|
||
|
/* schedule send lsreq */
|
||
|
if (on->request_list->count)
|
||
|
event_add_event(master, ospf6_lsreq_send, on, 0,
|
||
|
&on->thread_send_lsreq);
|
||
|
|
||
|
EVENT_OFF(on->thread_send_dbdesc);
|
||
|
event_add_event(master, ospf6_dbdesc_send_newone, on, 0,
|
||
|
&on->thread_send_dbdesc);
|
||
|
|
||
|
/* save last received dbdesc */
|
||
|
memcpy(&on->dbdesc_last, dbdesc, sizeof(struct ospf6_dbdesc));
|
||
|
}
|
||
|
|
||
|
static void ospf6_dbdesc_recv(struct in6_addr *src, struct in6_addr *dst,
|
||
|
struct ospf6_interface *oi,
|
||
|
struct ospf6_header *oh)
|
||
|
{
|
||
|
struct ospf6_neighbor *on;
|
||
|
struct ospf6_dbdesc *dbdesc;
|
||
|
|
||
|
on = ospf6_neighbor_lookup(oh->router_id, oi);
|
||
|
if (on == NULL) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Neighbor not found, ignore");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh
|
||
|
+ sizeof(struct ospf6_header));
|
||
|
|
||
|
if (((OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_AT) ==
|
||
|
OSPF6_OPT_AT) &&
|
||
|
(oi->at_data.flags == 0)) ||
|
||
|
((OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_AT) !=
|
||
|
OSPF6_OPT_AT) &&
|
||
|
(oi->at_data.flags != 0))) {
|
||
|
if (IS_OSPF6_DEBUG_AUTH_RX)
|
||
|
zlog_warn(
|
||
|
"VRF %s: IF %s AT-bit mismatch in dbdesc packet",
|
||
|
oi->interface->vrf->name, oi->interface->name);
|
||
|
oi->at_data.rx_drop++;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Interface MTU check */
|
||
|
if (!oi->mtu_ignore && ntohs(dbdesc->ifmtu) != oi->ifmtu) {
|
||
|
zlog_warn("VRF %s: I/F %s MTU mismatch (my %d rcvd %d)",
|
||
|
oi->interface->vrf->name, oi->interface->name,
|
||
|
oi->ifmtu, ntohs(dbdesc->ifmtu));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (dbdesc->reserved1 || dbdesc->reserved2) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug(
|
||
|
"Non-0 reserved field in %s's DbDesc, correct",
|
||
|
on->name);
|
||
|
dbdesc->reserved1 = 0;
|
||
|
dbdesc->reserved2 = 0;
|
||
|
}
|
||
|
|
||
|
oi->db_desc_in++;
|
||
|
|
||
|
if (ntohl(oh->router_id) < ntohl(oi->area->ospf6->router_id))
|
||
|
ospf6_dbdesc_recv_master(oh, on);
|
||
|
else if (ntohl(oi->area->ospf6->router_id) < ntohl(oh->router_id))
|
||
|
ospf6_dbdesc_recv_slave(oh, on);
|
||
|
else {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Can't decide which is master, ignore");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void ospf6_lsreq_recv(struct in6_addr *src, struct in6_addr *dst,
|
||
|
struct ospf6_interface *oi,
|
||
|
struct ospf6_header *oh)
|
||
|
{
|
||
|
struct ospf6_neighbor *on;
|
||
|
char *p;
|
||
|
struct ospf6_lsreq_entry *e;
|
||
|
struct ospf6_lsdb *lsdb = NULL;
|
||
|
struct ospf6_lsa *lsa;
|
||
|
|
||
|
on = ospf6_neighbor_lookup(oh->router_id, oi);
|
||
|
if (on == NULL) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Neighbor not found, ignore");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (on->state != OSPF6_NEIGHBOR_EXCHANGE
|
||
|
&& on->state != OSPF6_NEIGHBOR_LOADING
|
||
|
&& on->state != OSPF6_NEIGHBOR_FULL) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Neighbor state less than Exchange, ignore");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
oi->ls_req_in++;
|
||
|
|
||
|
/* Process each request */
|
||
|
for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header));
|
||
|
p + sizeof(struct ospf6_lsreq_entry) <= OSPF6_MESSAGE_END(oh);
|
||
|
p += sizeof(struct ospf6_lsreq_entry)) {
|
||
|
e = (struct ospf6_lsreq_entry *)p;
|
||
|
|
||
|
switch (OSPF6_LSA_SCOPE(e->type)) {
|
||
|
case OSPF6_SCOPE_LINKLOCAL:
|
||
|
lsdb = on->ospf6_if->lsdb;
|
||
|
break;
|
||
|
case OSPF6_SCOPE_AREA:
|
||
|
lsdb = on->ospf6_if->area->lsdb;
|
||
|
break;
|
||
|
case OSPF6_SCOPE_AS:
|
||
|
lsdb = on->ospf6_if->area->ospf6->lsdb;
|
||
|
break;
|
||
|
default:
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("Ignoring LSA of reserved scope");
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Find database copy */
|
||
|
lsa = ospf6_lsdb_lookup(e->type, e->id, e->adv_router, lsdb);
|
||
|
if (lsa == NULL) {
|
||
|
zlog_warn(
|
||
|
"Can't find requested lsa [%s Id:%pI4 Adv:%pI4] send badLSReq",
|
||
|
ospf6_lstype_name(e->type), &e->id,
|
||
|
&e->adv_router);
|
||
|
event_add_event(master, bad_lsreq, on, 0, NULL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->lsupdate_list);
|
||
|
}
|
||
|
|
||
|
assert(p == OSPF6_MESSAGE_END(oh));
|
||
|
|
||
|
/* schedule send lsupdate */
|
||
|
EVENT_OFF(on->thread_send_lsupdate);
|
||
|
event_add_event(master, ospf6_lsupdate_send_neighbor, on, 0,
|
||
|
&on->thread_send_lsupdate);
|
||
|
}
|
||
|
|
||
|
/* Verify, that the specified memory area contains exactly N valid IPv6
|
||
|
prefixes as specified by RFC5340, A.4.1. */
|
||
|
static unsigned ospf6_prefixes_examin(
|
||
|
struct ospf6_prefix *current, /* start of buffer */
|
||
|
unsigned length,
|
||
|
const uint32_t req_num_pfxs /* always compared with the actual number
|
||
|
of prefixes */
|
||
|
)
|
||
|
{
|
||
|
uint8_t requested_pfx_bytes;
|
||
|
uint32_t real_num_pfxs = 0;
|
||
|
|
||
|
while (length) {
|
||
|
if (length < OSPF6_PREFIX_MIN_SIZE) {
|
||
|
zlog_warn("%s: undersized IPv6 prefix header",
|
||
|
__func__);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
/* safe to look deeper */
|
||
|
if (current->prefix_length > IPV6_MAX_BITLEN) {
|
||
|
zlog_warn("%s: invalid PrefixLength (%u bits)",
|
||
|
__func__, current->prefix_length);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
/* covers both fixed- and variable-sized fields */
|
||
|
requested_pfx_bytes =
|
||
|
OSPF6_PREFIX_MIN_SIZE
|
||
|
+ OSPF6_PREFIX_SPACE(current->prefix_length);
|
||
|
if (requested_pfx_bytes > length) {
|
||
|
zlog_warn("%s: undersized IPv6 prefix", __func__);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
/* next prefix */
|
||
|
length -= requested_pfx_bytes;
|
||
|
current = (struct ospf6_prefix *)((caddr_t)current
|
||
|
+ requested_pfx_bytes);
|
||
|
real_num_pfxs++;
|
||
|
}
|
||
|
if (real_num_pfxs != req_num_pfxs) {
|
||
|
zlog_warn(
|
||
|
"%s: IPv6 prefix number mismatch (%u required, %u real)",
|
||
|
__func__, req_num_pfxs, real_num_pfxs);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
return MSG_OK;
|
||
|
}
|
||
|
|
||
|
/* Verify an LSA to have a valid length and dispatch further (where
|
||
|
appropriate) to check if the contents, including nested IPv6 prefixes,
|
||
|
is properly sized/aligned within the LSA. Note that this function gets
|
||
|
LSA type in network byte order, uses in host byte order and passes to
|
||
|
ospf6_lstype_name() in network byte order again. */
|
||
|
static unsigned ospf6_lsa_examin(struct ospf6_lsa_header *lsah,
|
||
|
const uint16_t lsalen,
|
||
|
const uint8_t headeronly)
|
||
|
{
|
||
|
struct ospf6_intra_prefix_lsa *intra_prefix_lsa;
|
||
|
struct ospf6_as_external_lsa *as_external_lsa;
|
||
|
struct ospf6_link_lsa *link_lsa;
|
||
|
unsigned exp_length;
|
||
|
uint8_t ltindex;
|
||
|
uint16_t lsatype;
|
||
|
|
||
|
/* In case an additional minimum length constraint is defined for
|
||
|
current
|
||
|
LSA type, make sure that this constraint is met. */
|
||
|
lsatype = ntohs(lsah->type);
|
||
|
ltindex = lsatype & OSPF6_LSTYPE_FCODE_MASK;
|
||
|
if (ltindex < OSPF6_LSTYPE_SIZE && ospf6_lsa_minlen[ltindex]
|
||
|
&& lsalen < ospf6_lsa_minlen[ltindex] + OSPF6_LSA_HEADER_SIZE) {
|
||
|
zlog_warn("%s: undersized (%u B) LSA", __func__, lsalen);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
switch (lsatype) {
|
||
|
case OSPF6_LSTYPE_ROUTER:
|
||
|
/* RFC5340 A.4.3, LSA header + OSPF6_ROUTER_LSA_MIN_SIZE bytes
|
||
|
followed
|
||
|
by N>=0 interface descriptions. */
|
||
|
if ((lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_ROUTER_LSA_MIN_SIZE)
|
||
|
% OSPF6_ROUTER_LSDESC_FIX_SIZE) {
|
||
|
zlog_warn(
|
||
|
"%s: Router LSA interface description alignment error",
|
||
|
__func__);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
break;
|
||
|
case OSPF6_LSTYPE_NETWORK:
|
||
|
/* RFC5340 A.4.4, LSA header + OSPF6_NETWORK_LSA_MIN_SIZE bytes
|
||
|
followed by N>=0 attached router descriptions. */
|
||
|
if ((lsalen - OSPF6_LSA_HEADER_SIZE
|
||
|
- OSPF6_NETWORK_LSA_MIN_SIZE)
|
||
|
% OSPF6_NETWORK_LSDESC_FIX_SIZE) {
|
||
|
zlog_warn(
|
||
|
"%s: Network LSA router description alignment error",
|
||
|
__func__);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
break;
|
||
|
case OSPF6_LSTYPE_INTER_PREFIX:
|
||
|
/* RFC5340 A.4.5, LSA header + OSPF6_INTER_PREFIX_LSA_MIN_SIZE
|
||
|
bytes
|
||
|
followed by 3-4 fields of a single IPv6 prefix. */
|
||
|
if (headeronly)
|
||
|
break;
|
||
|
return ospf6_prefixes_examin(
|
||
|
(struct ospf6_prefix
|
||
|
*)((caddr_t)lsah + OSPF6_LSA_HEADER_SIZE
|
||
|
+ OSPF6_INTER_PREFIX_LSA_MIN_SIZE),
|
||
|
lsalen - OSPF6_LSA_HEADER_SIZE
|
||
|
- OSPF6_INTER_PREFIX_LSA_MIN_SIZE,
|
||
|
1);
|
||
|
case OSPF6_LSTYPE_INTER_ROUTER:
|
||
|
/* RFC5340 A.4.6, fixed-size LSA. */
|
||
|
if (lsalen
|
||
|
> OSPF6_LSA_HEADER_SIZE + OSPF6_INTER_ROUTER_LSA_FIX_SIZE) {
|
||
|
zlog_warn("%s: Inter Router LSA oversized (%u B) LSA",
|
||
|
__func__, lsalen);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
break;
|
||
|
case OSPF6_LSTYPE_AS_EXTERNAL: /* RFC5340 A.4.7, same as A.4.8. */
|
||
|
case OSPF6_LSTYPE_TYPE_7:
|
||
|
/* RFC5340 A.4.8, LSA header + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE
|
||
|
bytes
|
||
|
followed by 3-4 fields of IPv6 prefix and 3 conditional LSA
|
||
|
fields:
|
||
|
16 bytes of forwarding address, 4 bytes of external route
|
||
|
tag,
|
||
|
4 bytes of referenced link state ID. */
|
||
|
if (headeronly)
|
||
|
break;
|
||
|
as_external_lsa =
|
||
|
(struct ospf6_as_external_lsa
|
||
|
*)((caddr_t)lsah + OSPF6_LSA_HEADER_SIZE);
|
||
|
exp_length =
|
||
|
OSPF6_LSA_HEADER_SIZE + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE;
|
||
|
/* To find out if the last optional field (Referenced Link State
|
||
|
ID) is
|
||
|
assumed in this LSA, we need to access fixed fields of the
|
||
|
IPv6
|
||
|
prefix before ospf6_prefix_examin() confirms its sizing. */
|
||
|
if (exp_length + OSPF6_PREFIX_MIN_SIZE > lsalen) {
|
||
|
zlog_warn(
|
||
|
"%s: AS External undersized (%u B) LSA header",
|
||
|
__func__, lsalen);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
/* forwarding address */
|
||
|
if (CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F))
|
||
|
exp_length += 16;
|
||
|
/* external route tag */
|
||
|
if (CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T))
|
||
|
exp_length += 4;
|
||
|
/* referenced link state ID */
|
||
|
if (as_external_lsa->prefix.u._prefix_referenced_lstype)
|
||
|
exp_length += 4;
|
||
|
/* All the fixed-size fields (mandatory and optional) must fit.
|
||
|
I.e.,
|
||
|
this check does not include any IPv6 prefix fields. */
|
||
|
if (exp_length > lsalen) {
|
||
|
zlog_warn(
|
||
|
"%s: AS External undersized (%u B) LSA header",
|
||
|
__func__, lsalen);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
/* The last call completely covers the remainder (IPv6 prefix).
|
||
|
*/
|
||
|
return ospf6_prefixes_examin(
|
||
|
(struct ospf6_prefix
|
||
|
*)((caddr_t)as_external_lsa
|
||
|
+ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE),
|
||
|
lsalen - exp_length, 1);
|
||
|
case OSPF6_LSTYPE_LINK:
|
||
|
/* RFC5340 A.4.9, LSA header + OSPF6_LINK_LSA_MIN_SIZE bytes
|
||
|
followed
|
||
|
by N>=0 IPv6 prefix blocks (with N declared beforehand). */
|
||
|
if (headeronly)
|
||
|
break;
|
||
|
link_lsa = (struct ospf6_link_lsa *)((caddr_t)lsah
|
||
|
+ OSPF6_LSA_HEADER_SIZE);
|
||
|
return ospf6_prefixes_examin(
|
||
|
(struct ospf6_prefix *)((caddr_t)link_lsa
|
||
|
+ OSPF6_LINK_LSA_MIN_SIZE),
|
||
|
lsalen - OSPF6_LSA_HEADER_SIZE
|
||
|
- OSPF6_LINK_LSA_MIN_SIZE,
|
||
|
ntohl(link_lsa->prefix_num) /* 32 bits */
|
||
|
);
|
||
|
case OSPF6_LSTYPE_INTRA_PREFIX:
|
||
|
/* RFC5340 A.4.10, LSA header + OSPF6_INTRA_PREFIX_LSA_MIN_SIZE
|
||
|
bytes
|
||
|
followed by N>=0 IPv6 prefixes (with N declared beforehand).
|
||
|
*/
|
||
|
if (headeronly)
|
||
|
break;
|
||
|
intra_prefix_lsa =
|
||
|
(struct ospf6_intra_prefix_lsa
|
||
|
*)((caddr_t)lsah + OSPF6_LSA_HEADER_SIZE);
|
||
|
return ospf6_prefixes_examin(
|
||
|
(struct ospf6_prefix
|
||
|
*)((caddr_t)intra_prefix_lsa
|
||
|
+ OSPF6_INTRA_PREFIX_LSA_MIN_SIZE),
|
||
|
lsalen - OSPF6_LSA_HEADER_SIZE
|
||
|
- OSPF6_INTRA_PREFIX_LSA_MIN_SIZE,
|
||
|
ntohs(intra_prefix_lsa->prefix_num) /* 16 bits */
|
||
|
);
|
||
|
case OSPF6_LSTYPE_GRACE_LSA:
|
||
|
if (lsalen < OSPF6_LSA_HEADER_SIZE + GRACE_PERIOD_TLV_SIZE
|
||
|
+ GRACE_RESTART_REASON_TLV_SIZE) {
|
||
|
if (IS_DEBUG_OSPF6_GR)
|
||
|
zlog_debug("%s: Undersized GraceLSA.",
|
||
|
__func__);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
}
|
||
|
/* No additional validation is possible for unknown LSA types, which are
|
||
|
themselves valid in OPSFv3, hence the default decision is to accept.
|
||
|
*/
|
||
|
return MSG_OK;
|
||
|
}
|
||
|
|
||
|
/* 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
|
||
|
ospf6_lsaseq_examin(struct ospf6_lsa_header *lsah, /* start of buffered data */
|
||
|
size_t length, const uint8_t headeronly,
|
||
|
/* When declared_num_lsas is not 0, compare it to the real
|
||
|
number of LSAs
|
||
|
and treat the difference as an error. */
|
||
|
const uint32_t declared_num_lsas)
|
||
|
{
|
||
|
uint32_t counted_lsas = 0;
|
||
|
|
||
|
while (length) {
|
||
|
uint16_t lsalen;
|
||
|
if (length < OSPF6_LSA_HEADER_SIZE) {
|
||
|
zlog_warn(
|
||
|
"%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 = ospf6_lsa_size(lsah);
|
||
|
if (lsalen < OSPF6_LSA_HEADER_SIZE) {
|
||
|
zlog_warn(
|
||
|
"%s: malformed LSA header #%u, declared length is %u B",
|
||
|
__func__, counted_lsas, lsalen);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
if (headeronly) {
|
||
|
/* less checks here and in ospf6_lsa_examin() */
|
||
|
if (MSG_OK != ospf6_lsa_examin(lsah, lsalen, 1)) {
|
||
|
zlog_warn(
|
||
|
"%s: anomaly in header-only %s LSA #%u",
|
||
|
__func__, ospf6_lstype_name(lsah->type),
|
||
|
counted_lsas);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
lsah = (struct ospf6_lsa_header
|
||
|
*)((caddr_t)lsah
|
||
|
+ OSPF6_LSA_HEADER_SIZE);
|
||
|
length -= OSPF6_LSA_HEADER_SIZE;
|
||
|
} else {
|
||
|
/* make sure the input buffer is deep enough before
|
||
|
* further checks */
|
||
|
if (lsalen > length) {
|
||
|
zlog_warn(
|
||
|
"%s: anomaly in %s LSA #%u: declared length is %u B, buffered length is %zu B",
|
||
|
__func__, ospf6_lstype_name(lsah->type),
|
||
|
counted_lsas, lsalen, length);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
if (MSG_OK != ospf6_lsa_examin(lsah, lsalen, 0)) {
|
||
|
zlog_warn("%s: anomaly in %s LSA #%u", __func__,
|
||
|
ospf6_lstype_name(lsah->type),
|
||
|
counted_lsas);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
lsah = (struct ospf6_lsa_header *)((caddr_t)lsah
|
||
|
+ lsalen);
|
||
|
length -= lsalen;
|
||
|
}
|
||
|
counted_lsas++;
|
||
|
}
|
||
|
|
||
|
if (declared_num_lsas && counted_lsas != declared_num_lsas) {
|
||
|
zlog_warn("%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 ospf6_packet_examin(struct ospf6_header *oh,
|
||
|
const unsigned bytesonwire)
|
||
|
{
|
||
|
struct ospf6_lsupdate *lsupd;
|
||
|
unsigned test;
|
||
|
|
||
|
/* length, 1st approximation */
|
||
|
if (bytesonwire < OSPF6_HEADER_SIZE) {
|
||
|
zlog_warn("%s: undersized (%u B) packet", __func__,
|
||
|
bytesonwire);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
|
||
|
/* Now it is safe to access header fields. */
|
||
|
if (bytesonwire != ntohs(oh->length)) {
|
||
|
zlog_warn("%s: %s packet length error (%u real, %u declared)",
|
||
|
__func__, ospf6_message_type(oh->type), bytesonwire,
|
||
|
ntohs(oh->length));
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
|
||
|
/* version check */
|
||
|
if (oh->version != OSPFV3_VERSION) {
|
||
|
zlog_warn("%s: invalid (%u) protocol version", __func__,
|
||
|
oh->version);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
/* length, 2nd approximation */
|
||
|
if (oh->type < OSPF6_MESSAGE_TYPE_ALL && ospf6_packet_minlen[oh->type]
|
||
|
&& bytesonwire
|
||
|
< OSPF6_HEADER_SIZE + ospf6_packet_minlen[oh->type]) {
|
||
|
zlog_warn("%s: undersized (%u B) %s packet", __func__,
|
||
|
bytesonwire, ospf6_message_type(oh->type));
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
/* type-specific deeper validation */
|
||
|
switch (oh->type) {
|
||
|
case OSPF6_MESSAGE_TYPE_HELLO:
|
||
|
/* RFC5340 A.3.2, packet header + OSPF6_HELLO_MIN_SIZE bytes
|
||
|
followed
|
||
|
by N>=0 router-IDs. */
|
||
|
if (0
|
||
|
== (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_HELLO_MIN_SIZE)
|
||
|
% 4)
|
||
|
return MSG_OK;
|
||
|
zlog_warn("%s: alignment error in %s packet", __func__,
|
||
|
ospf6_message_type(oh->type));
|
||
|
return MSG_NG;
|
||
|
case OSPF6_MESSAGE_TYPE_DBDESC:
|
||
|
/* RFC5340 A.3.3, packet header + OSPF6_DB_DESC_MIN_SIZE bytes
|
||
|
followed
|
||
|
by N>=0 header-only LSAs. */
|
||
|
test = ospf6_lsaseq_examin(
|
||
|
(struct ospf6_lsa_header *)((caddr_t)oh
|
||
|
+ OSPF6_HEADER_SIZE
|
||
|
+ OSPF6_DB_DESC_MIN_SIZE),
|
||
|
bytesonwire - OSPF6_HEADER_SIZE
|
||
|
- OSPF6_DB_DESC_MIN_SIZE,
|
||
|
1, 0);
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_LSREQ:
|
||
|
/* RFC5340 A.3.4, packet header + N>=0 LS description blocks. */
|
||
|
if (0
|
||
|
== (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_REQ_MIN_SIZE)
|
||
|
% OSPF6_LSREQ_LSDESC_FIX_SIZE)
|
||
|
return MSG_OK;
|
||
|
zlog_warn("%s: alignment error in %s packet", __func__,
|
||
|
ospf6_message_type(oh->type));
|
||
|
return MSG_NG;
|
||
|
case OSPF6_MESSAGE_TYPE_LSUPDATE:
|
||
|
/* RFC5340 A.3.5, packet header + OSPF6_LS_UPD_MIN_SIZE bytes
|
||
|
followed
|
||
|
by N>=0 full LSAs (with N declared beforehand). */
|
||
|
lsupd = (struct ospf6_lsupdate *)((caddr_t)oh
|
||
|
+ OSPF6_HEADER_SIZE);
|
||
|
test = ospf6_lsaseq_examin(
|
||
|
(struct ospf6_lsa_header *)((caddr_t)lsupd
|
||
|
+ OSPF6_LS_UPD_MIN_SIZE),
|
||
|
bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_UPD_MIN_SIZE,
|
||
|
0, ntohl(lsupd->lsa_number) /* 32 bits */
|
||
|
);
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_LSACK:
|
||
|
/* RFC5340 A.3.6, packet header + N>=0 header-only LSAs. */
|
||
|
test = ospf6_lsaseq_examin(
|
||
|
(struct ospf6_lsa_header *)((caddr_t)oh
|
||
|
+ OSPF6_HEADER_SIZE
|
||
|
+ OSPF6_LS_ACK_MIN_SIZE),
|
||
|
bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_ACK_MIN_SIZE,
|
||
|
1, 0);
|
||
|
break;
|
||
|
default:
|
||
|
zlog_warn("%s: invalid (%u) message type", __func__, oh->type);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
if (test != MSG_OK)
|
||
|
zlog_warn("%s: anomaly in %s packet", __func__,
|
||
|
ospf6_message_type(oh->type));
|
||
|
return test;
|
||
|
}
|
||
|
|
||
|
/* Verify particular fields of otherwise correct received OSPF packet to
|
||
|
meet the requirements of RFC. */
|
||
|
static int ospf6_rxpacket_examin(struct ospf6_interface *oi,
|
||
|
struct ospf6_header *oh,
|
||
|
const unsigned bytesonwire)
|
||
|
{
|
||
|
struct ospf6_neighbor *on;
|
||
|
|
||
|
if (MSG_OK != ospf6_packet_examin(oh, bytesonwire))
|
||
|
return MSG_NG;
|
||
|
|
||
|
on = ospf6_neighbor_lookup(oh->router_id, oi);
|
||
|
|
||
|
/* Area-ID check */
|
||
|
if (oh->area_id != oi->area->area_id) {
|
||
|
if (oh->area_id == OSPF_AREA_BACKBONE)
|
||
|
zlog_warn(
|
||
|
"VRF %s: I/F %s (%s, Router-ID: %pI4) Message may be via Virtual Link: not supported",
|
||
|
oi->interface->vrf->name, oi->interface->name,
|
||
|
on ? on->name : "null", &oh->router_id);
|
||
|
else
|
||
|
zlog_warn(
|
||
|
"VRF %s: I/F %s (%s, Router-ID: %pI4) Area-ID mismatch (my %pI4, rcvd %pI4)",
|
||
|
oi->interface->vrf->name, oi->interface->name,
|
||
|
on ? on->name : "null", &oh->router_id,
|
||
|
&oi->area->area_id, &oh->area_id);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
|
||
|
/* Instance-ID check */
|
||
|
if (oh->instance_id != oi->instance_id) {
|
||
|
zlog_warn(
|
||
|
"VRF %s: I/F %s (%s, Router-ID: %pI4) Instance-ID mismatch (my %u, rcvd %u)",
|
||
|
oi->interface->vrf->name, oi->interface->name,
|
||
|
on ? on->name : "null", &oh->router_id, oi->instance_id,
|
||
|
oh->instance_id);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
|
||
|
/* Router-ID check */
|
||
|
if (oh->router_id == oi->area->ospf6->router_id) {
|
||
|
zlog_warn("VRF %s: I/F %s Duplicate Router-ID (%pI4)",
|
||
|
oi->interface->vrf->name, oi->interface->name,
|
||
|
&oh->router_id);
|
||
|
return MSG_NG;
|
||
|
}
|
||
|
return MSG_OK;
|
||
|
}
|
||
|
|
||
|
static void ospf6_lsupdate_recv(struct in6_addr *src, struct in6_addr *dst,
|
||
|
struct ospf6_interface *oi,
|
||
|
struct ospf6_header *oh)
|
||
|
{
|
||
|
struct ospf6_neighbor *on;
|
||
|
struct ospf6_lsupdate *lsupdate;
|
||
|
char *p;
|
||
|
|
||
|
on = ospf6_neighbor_lookup(oh->router_id, oi);
|
||
|
if (on == NULL) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Neighbor not found, ignore");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (on->state != OSPF6_NEIGHBOR_EXCHANGE
|
||
|
&& on->state != OSPF6_NEIGHBOR_LOADING
|
||
|
&& on->state != OSPF6_NEIGHBOR_FULL) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Neighbor state less than Exchange, ignore");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
|
||
|
+ sizeof(struct ospf6_header));
|
||
|
|
||
|
oi->ls_upd_in++;
|
||
|
|
||
|
/* Process LSAs */
|
||
|
for (p = (char *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
|
||
|
p < OSPF6_MESSAGE_END(oh) &&
|
||
|
p + ospf6_lsa_size((struct ospf6_lsa_header *)p) <=
|
||
|
OSPF6_MESSAGE_END(oh);
|
||
|
p += ospf6_lsa_size((struct ospf6_lsa_header *)p)) {
|
||
|
ospf6_receive_lsa(on, (struct ospf6_lsa_header *)p);
|
||
|
}
|
||
|
|
||
|
assert(p == OSPF6_MESSAGE_END(oh));
|
||
|
}
|
||
|
|
||
|
static void ospf6_lsack_recv(struct in6_addr *src, struct in6_addr *dst,
|
||
|
struct ospf6_interface *oi,
|
||
|
struct ospf6_header *oh)
|
||
|
{
|
||
|
struct ospf6_neighbor *on;
|
||
|
char *p;
|
||
|
struct ospf6_lsa *his, *mine;
|
||
|
struct ospf6_lsdb *lsdb = NULL;
|
||
|
|
||
|
assert(oh->type == OSPF6_MESSAGE_TYPE_LSACK);
|
||
|
|
||
|
on = ospf6_neighbor_lookup(oh->router_id, oi);
|
||
|
if (on == NULL) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Neighbor not found, ignore");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (on->state != OSPF6_NEIGHBOR_EXCHANGE
|
||
|
&& on->state != OSPF6_NEIGHBOR_LOADING
|
||
|
&& on->state != OSPF6_NEIGHBOR_FULL) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
|
||
|
zlog_debug("Neighbor state less than Exchange, ignore");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
oi->ls_ack_in++;
|
||
|
|
||
|
for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header));
|
||
|
p + sizeof(struct ospf6_lsa_header) <= OSPF6_MESSAGE_END(oh);
|
||
|
p += sizeof(struct ospf6_lsa_header)) {
|
||
|
his = ospf6_lsa_create_headeronly((struct ospf6_lsa_header *)p);
|
||
|
|
||
|
switch (OSPF6_LSA_SCOPE(his->header->type)) {
|
||
|
case OSPF6_SCOPE_LINKLOCAL:
|
||
|
lsdb = on->ospf6_if->lsdb;
|
||
|
break;
|
||
|
case OSPF6_SCOPE_AREA:
|
||
|
lsdb = on->ospf6_if->area->lsdb;
|
||
|
break;
|
||
|
case OSPF6_SCOPE_AS:
|
||
|
lsdb = on->ospf6_if->area->ospf6->lsdb;
|
||
|
break;
|
||
|
case OSPF6_SCOPE_RESERVED:
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("Ignoring LSA of reserved scope");
|
||
|
ospf6_lsa_delete(his);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("%s acknowledged by %s", his->name,
|
||
|
on->name);
|
||
|
|
||
|
/* Find database copy */
|
||
|
mine = ospf6_lsdb_lookup(his->header->type, his->header->id,
|
||
|
his->header->adv_router, lsdb);
|
||
|
if (mine == NULL) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("No database copy");
|
||
|
ospf6_lsa_delete(his);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Check if the LSA is on his retrans-list */
|
||
|
mine = ospf6_lsdb_lookup(his->header->type, his->header->id,
|
||
|
his->header->adv_router,
|
||
|
on->retrans_list);
|
||
|
if (mine == NULL) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("Not on %s's retrans-list",
|
||
|
on->name);
|
||
|
ospf6_lsa_delete(his);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (ospf6_lsa_compare(his, mine) != 0) {
|
||
|
/* Log this questionable acknowledgement,
|
||
|
and examine the next one. */
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug("Questionable acknowledgement");
|
||
|
ospf6_lsa_delete(his);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
|
||
|
zlog_debug(
|
||
|
"Acknowledged, remove from %s's retrans-list",
|
||
|
on->name);
|
||
|
|
||
|
ospf6_decrement_retrans_count(mine);
|
||
|
if (OSPF6_LSA_IS_MAXAGE(mine))
|
||
|
ospf6_maxage_remove(on->ospf6_if->area->ospf6);
|
||
|
ospf6_lsdb_remove(mine, on->retrans_list);
|
||
|
ospf6_lsa_delete(his);
|
||
|
}
|
||
|
|
||
|
assert(p == OSPF6_MESSAGE_END(oh));
|
||
|
}
|
||
|
|
||
|
static uint8_t *recvbuf = NULL;
|
||
|
static uint8_t *sendbuf = NULL;
|
||
|
static unsigned int iobuflen = 0;
|
||
|
|
||
|
int ospf6_iobuf_size(unsigned int size)
|
||
|
{
|
||
|
/* NB: there was previously code here that tried to dynamically size
|
||
|
* the buffer for whatever we see in MTU on interfaces. Which is
|
||
|
* _unconditionally wrong_ - we can always receive fragmented IPv6
|
||
|
* up to the regular 64k length limit. (No jumbograms, thankfully.)
|
||
|
*/
|
||
|
|
||
|
if (!iobuflen) {
|
||
|
/* the + 128 is to have some runway at the end */
|
||
|
size_t alloc_size = 65536 + 128;
|
||
|
|
||
|
assert(!recvbuf && !sendbuf);
|
||
|
|
||
|
recvbuf = XMALLOC(MTYPE_OSPF6_MESSAGE, alloc_size);
|
||
|
sendbuf = XMALLOC(MTYPE_OSPF6_MESSAGE, alloc_size);
|
||
|
iobuflen = alloc_size;
|
||
|
}
|
||
|
|
||
|
return iobuflen;
|
||
|
}
|
||
|
|
||
|
void ospf6_message_terminate(void)
|
||
|
{
|
||
|
XFREE(MTYPE_OSPF6_MESSAGE, recvbuf);
|
||
|
XFREE(MTYPE_OSPF6_MESSAGE, sendbuf);
|
||
|
|
||
|
iobuflen = 0;
|
||
|
}
|
||
|
|
||
|
enum ospf6_read_return_enum {
|
||
|
OSPF6_READ_ERROR,
|
||
|
OSPF6_READ_CONTINUE,
|
||
|
};
|
||
|
|
||
|
static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6)
|
||
|
{
|
||
|
int len;
|
||
|
struct in6_addr src, dst;
|
||
|
ifindex_t ifindex;
|
||
|
struct iovec iovector[2];
|
||
|
struct ospf6_interface *oi;
|
||
|
struct ospf6_header *oh;
|
||
|
enum ospf6_auth_err ret = OSPF6_AUTH_PROCESS_NORMAL;
|
||
|
uint32_t at_len = 0;
|
||
|
uint32_t lls_len = 0;
|
||
|
|
||
|
/* initialize */
|
||
|
memset(&src, 0, sizeof(src));
|
||
|
memset(&dst, 0, sizeof(dst));
|
||
|
ifindex = 0;
|
||
|
iovector[0].iov_base = recvbuf;
|
||
|
iovector[0].iov_len = iobuflen;
|
||
|
iovector[1].iov_base = NULL;
|
||
|
iovector[1].iov_len = 0;
|
||
|
|
||
|
/* receive message */
|
||
|
len = ospf6_recvmsg(&src, &dst, &ifindex, iovector, sockfd);
|
||
|
if (len < 0)
|
||
|
return OSPF6_READ_ERROR;
|
||
|
|
||
|
if ((uint)len > iobuflen) {
|
||
|
flog_err(EC_LIB_DEVELOPMENT, "Excess message read");
|
||
|
return OSPF6_READ_ERROR;
|
||
|
}
|
||
|
|
||
|
/* ensure some zeroes past the end, just as a security precaution */
|
||
|
memset(recvbuf + len, 0, MIN(128, iobuflen - len));
|
||
|
|
||
|
oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id);
|
||
|
if (oi == NULL || oi->area == NULL
|
||
|
|| CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
|
||
|
RECV_HDR))
|
||
|
zlog_debug("Message received on disabled interface");
|
||
|
return OSPF6_READ_CONTINUE;
|
||
|
}
|
||
|
if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
|
||
|
RECV_HDR))
|
||
|
zlog_debug("%s: Ignore message on passive interface %s",
|
||
|
__func__, oi->interface->name);
|
||
|
return OSPF6_READ_CONTINUE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Drop packet destined to another VRF.
|
||
|
* This happens when raw_l3mdev_accept is set to 1.
|
||
|
*/
|
||
|
if (ospf6->vrf_id != oi->interface->vrf->vrf_id)
|
||
|
return OSPF6_READ_CONTINUE;
|
||
|
|
||
|
oh = (struct ospf6_header *)recvbuf;
|
||
|
ret = ospf6_auth_validate_pkt(oi, (uint32_t *)&len, oh, &at_len,
|
||
|
&lls_len);
|
||
|
if (ret == OSPF6_AUTH_VALIDATE_SUCCESS) {
|
||
|
ret = ospf6_auth_check_digest(oh, oi, &src, lls_len);
|
||
|
if (ret == OSPF6_AUTH_VALIDATE_FAILURE) {
|
||
|
if (IS_OSPF6_DEBUG_AUTH_RX)
|
||
|
zlog_err(
|
||
|
"RECV[%s]: OSPF packet auth digest miss-match on %s",
|
||
|
oi->interface->name,
|
||
|
ospf6_message_type(oh->type));
|
||
|
oi->at_data.rx_drop++;
|
||
|
return OSPF6_READ_CONTINUE;
|
||
|
}
|
||
|
} else if (ret == OSPF6_AUTH_VALIDATE_FAILURE) {
|
||
|
oi->at_data.rx_drop++;
|
||
|
return OSPF6_READ_CONTINUE;
|
||
|
}
|
||
|
|
||
|
if (ospf6_rxpacket_examin(oi, oh, len) != MSG_OK)
|
||
|
return OSPF6_READ_CONTINUE;
|
||
|
|
||
|
/* Being here means, that no sizing/alignment issues were detected in
|
||
|
the input packet. This renders the additional checks performed below
|
||
|
and also in the type-specific dispatching functions a dead code,
|
||
|
which can be dismissed in a cleanup-focused review round later. */
|
||
|
|
||
|
/* Log */
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) {
|
||
|
zlog_debug("%s received on %s", ospf6_message_type(oh->type),
|
||
|
oi->interface->name);
|
||
|
zlog_debug(" src: %pI6", &src);
|
||
|
zlog_debug(" dst: %pI6", &dst);
|
||
|
|
||
|
switch (oh->type) {
|
||
|
case OSPF6_MESSAGE_TYPE_HELLO:
|
||
|
ospf6_hello_print(oh, OSPF6_ACTION_RECV);
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_DBDESC:
|
||
|
ospf6_dbdesc_print(oh, OSPF6_ACTION_RECV);
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_LSREQ:
|
||
|
ospf6_lsreq_print(oh, OSPF6_ACTION_RECV);
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_LSUPDATE:
|
||
|
ospf6_lsupdate_print(oh, OSPF6_ACTION_RECV);
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_LSACK:
|
||
|
ospf6_lsack_print(oh, OSPF6_ACTION_RECV);
|
||
|
break;
|
||
|
default:
|
||
|
assert(0);
|
||
|
}
|
||
|
|
||
|
if ((at_len != 0) && IS_OSPF6_DEBUG_AUTH_RX)
|
||
|
ospf6_auth_hdr_dump_recv(oh, (len + at_len + lls_len),
|
||
|
lls_len);
|
||
|
}
|
||
|
|
||
|
switch (oh->type) {
|
||
|
case OSPF6_MESSAGE_TYPE_HELLO:
|
||
|
ospf6_hello_recv(&src, &dst, oi, oh);
|
||
|
break;
|
||
|
|
||
|
case OSPF6_MESSAGE_TYPE_DBDESC:
|
||
|
ospf6_dbdesc_recv(&src, &dst, oi, oh);
|
||
|
break;
|
||
|
|
||
|
case OSPF6_MESSAGE_TYPE_LSREQ:
|
||
|
ospf6_lsreq_recv(&src, &dst, oi, oh);
|
||
|
break;
|
||
|
|
||
|
case OSPF6_MESSAGE_TYPE_LSUPDATE:
|
||
|
ospf6_lsupdate_recv(&src, &dst, oi, oh);
|
||
|
break;
|
||
|
|
||
|
case OSPF6_MESSAGE_TYPE_LSACK:
|
||
|
ospf6_lsack_recv(&src, &dst, oi, oh);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
assert(0);
|
||
|
}
|
||
|
|
||
|
return OSPF6_READ_CONTINUE;
|
||
|
}
|
||
|
|
||
|
void ospf6_receive(struct event *thread)
|
||
|
{
|
||
|
int sockfd;
|
||
|
struct ospf6 *ospf6;
|
||
|
int count = 0;
|
||
|
|
||
|
/* add next read thread */
|
||
|
ospf6 = EVENT_ARG(thread);
|
||
|
sockfd = EVENT_FD(thread);
|
||
|
|
||
|
event_add_read(master, ospf6_receive, ospf6, ospf6->fd,
|
||
|
&ospf6->t_ospf6_receive);
|
||
|
|
||
|
while (count < ospf6->write_oi_count) {
|
||
|
count++;
|
||
|
switch (ospf6_read_helper(sockfd, ospf6)) {
|
||
|
case OSPF6_READ_ERROR:
|
||
|
return;
|
||
|
case OSPF6_READ_CONTINUE:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void ospf6_fill_hdr_checksum(struct ospf6_interface *oi,
|
||
|
struct ospf6_packet *op)
|
||
|
{
|
||
|
struct ipv6_ph ph = {};
|
||
|
struct ospf6_header *oh;
|
||
|
void *offset = NULL;
|
||
|
|
||
|
if (oi->at_data.flags != 0)
|
||
|
return;
|
||
|
|
||
|
memcpy(&ph.src, oi->linklocal_addr, sizeof(struct in6_addr));
|
||
|
memcpy(&ph.dst, &op->dst, sizeof(struct in6_addr));
|
||
|
ph.ulpl = htonl(op->length);
|
||
|
ph.next_hdr = IPPROTO_OSPFIGP;
|
||
|
|
||
|
/* Suppress static analysis warnings about accessing icmp6 oob */
|
||
|
oh = (struct ospf6_header *)STREAM_DATA(op->s);
|
||
|
offset = oh;
|
||
|
oh->checksum = in_cksum_with_ph6(&ph, offset, op->length);
|
||
|
}
|
||
|
|
||
|
static void ospf6_make_header(uint8_t type, struct ospf6_interface *oi,
|
||
|
struct stream *s)
|
||
|
{
|
||
|
struct ospf6_header *oh;
|
||
|
|
||
|
oh = (struct ospf6_header *)STREAM_DATA(s);
|
||
|
|
||
|
oh->version = (uint8_t)OSPFV3_VERSION;
|
||
|
oh->type = type;
|
||
|
oh->length = 0;
|
||
|
|
||
|
oh->router_id = oi->area->ospf6->router_id;
|
||
|
oh->area_id = oi->area->area_id;
|
||
|
oh->checksum = 0;
|
||
|
oh->instance_id = oi->instance_id;
|
||
|
oh->reserved = 0;
|
||
|
|
||
|
stream_forward_endp(s, OSPF6_HEADER_SIZE);
|
||
|
}
|
||
|
|
||
|
static void ospf6_fill_header(struct ospf6_interface *oi, struct stream *s,
|
||
|
uint16_t length)
|
||
|
{
|
||
|
struct ospf6_header *oh;
|
||
|
|
||
|
oh = (struct ospf6_header *)STREAM_DATA(s);
|
||
|
|
||
|
oh->length = htons(length);
|
||
|
}
|
||
|
|
||
|
static void ospf6_fill_lsupdate_header(struct stream *s, uint32_t lsa_num)
|
||
|
{
|
||
|
struct ospf6_header *oh;
|
||
|
struct ospf6_lsupdate *lsu;
|
||
|
|
||
|
oh = (struct ospf6_header *)STREAM_DATA(s);
|
||
|
|
||
|
lsu = (struct ospf6_lsupdate *)((caddr_t)oh
|
||
|
+ sizeof(struct ospf6_header));
|
||
|
lsu->lsa_number = htonl(lsa_num);
|
||
|
}
|
||
|
|
||
|
static void ospf6_auth_trailer_copy_keychain_key(struct ospf6_interface *oi)
|
||
|
{
|
||
|
char *keychain_name = NULL;
|
||
|
struct keychain *keychain = NULL;
|
||
|
struct key *key = NULL;
|
||
|
|
||
|
keychain_name = oi->at_data.keychain;
|
||
|
keychain = keychain_lookup(keychain_name);
|
||
|
if (keychain) {
|
||
|
key = key_lookup_for_send(keychain);
|
||
|
if (key && key->string &&
|
||
|
key->hash_algo != KEYCHAIN_ALGO_NULL) {
|
||
|
/* storing the values so that further
|
||
|
* lookup can be avoided. after
|
||
|
* processing the digest need to reset
|
||
|
* these values
|
||
|
*/
|
||
|
oi->at_data.hash_algo = key->hash_algo;
|
||
|
if (oi->at_data.auth_key)
|
||
|
XFREE(MTYPE_OSPF6_AUTH_MANUAL_KEY,
|
||
|
oi->at_data.auth_key);
|
||
|
oi->at_data.auth_key = XSTRDUP(
|
||
|
MTYPE_OSPF6_AUTH_MANUAL_KEY, key->string);
|
||
|
oi->at_data.key_id = key->index;
|
||
|
SET_FLAG(oi->at_data.flags,
|
||
|
OSPF6_AUTH_TRAILER_KEYCHAIN_VALID);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static uint16_t ospf6_packet_max(struct ospf6_interface *oi)
|
||
|
{
|
||
|
uint16_t at_len = 0;
|
||
|
|
||
|
assert(oi->ifmtu > sizeof(struct ip6_hdr));
|
||
|
|
||
|
if (oi->at_data.flags != 0) {
|
||
|
if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN))
|
||
|
ospf6_auth_trailer_copy_keychain_key(oi);
|
||
|
|
||
|
at_len += OSPF6_AUTH_HDR_MIN_SIZE;
|
||
|
at_len += keychain_get_hash_len(oi->at_data.hash_algo);
|
||
|
return oi->ifmtu - (sizeof(struct ip6_hdr)) - at_len;
|
||
|
}
|
||
|
|
||
|
return oi->ifmtu - (sizeof(struct ip6_hdr));
|
||
|
}
|
||
|
|
||
|
static uint16_t ospf6_make_hello(struct ospf6_interface *oi, struct stream *s)
|
||
|
{
|
||
|
struct listnode *node, *nnode;
|
||
|
struct ospf6_neighbor *on;
|
||
|
uint16_t length = OSPF6_HELLO_MIN_SIZE;
|
||
|
uint8_t options1 = oi->area->options[1];
|
||
|
|
||
|
if (oi->at_data.flags != 0)
|
||
|
options1 |= OSPF6_OPT_AT;
|
||
|
|
||
|
stream_putl(s, oi->interface->ifindex);
|
||
|
stream_putc(s, oi->priority);
|
||
|
stream_putc(s, oi->area->options[0]);
|
||
|
stream_putc(s, options1);
|
||
|
stream_putc(s, oi->area->options[2]);
|
||
|
stream_putw(s, oi->hello_interval);
|
||
|
stream_putw(s, oi->dead_interval);
|
||
|
stream_put_ipv4(s, oi->drouter);
|
||
|
stream_put_ipv4(s, oi->bdrouter);
|
||
|
|
||
|
for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) {
|
||
|
if (on->state < OSPF6_NEIGHBOR_INIT)
|
||
|
continue;
|
||
|
|
||
|
if ((length + sizeof(uint32_t) + OSPF6_HEADER_SIZE)
|
||
|
> ospf6_packet_max(oi)) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO,
|
||
|
SEND))
|
||
|
zlog_debug(
|
||
|
"sending Hello message: exceeds I/F MTU");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
stream_put_ipv4(s, on->router_id);
|
||
|
length += sizeof(uint32_t);
|
||
|
}
|
||
|
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
static void ospf6_write(struct event *thread)
|
||
|
{
|
||
|
struct ospf6 *ospf6 = EVENT_ARG(thread);
|
||
|
struct ospf6_interface *oi;
|
||
|
struct ospf6_header *oh;
|
||
|
struct ospf6_packet *op;
|
||
|
struct listnode *node;
|
||
|
struct iovec iovector[2];
|
||
|
int pkt_count = 0;
|
||
|
int len;
|
||
|
int64_t latency = 0;
|
||
|
struct timeval timestamp;
|
||
|
uint16_t at_len = 0;
|
||
|
|
||
|
if (ospf6->fd < 0) {
|
||
|
zlog_warn("ospf6_write failed to send, fd %d", ospf6->fd);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
node = listhead(ospf6->oi_write_q);
|
||
|
assert(node);
|
||
|
oi = listgetdata(node);
|
||
|
|
||
|
while ((pkt_count < ospf6->write_oi_count) && oi) {
|
||
|
op = ospf6_fifo_head(oi->obuf);
|
||
|
assert(op);
|
||
|
assert(op->length >= OSPF6_HEADER_SIZE);
|
||
|
|
||
|
iovector[0].iov_base = (caddr_t)stream_pnt(op->s);
|
||
|
iovector[0].iov_len = op->length;
|
||
|
iovector[1].iov_base = NULL;
|
||
|
iovector[1].iov_len = 0;
|
||
|
|
||
|
oh = (struct ospf6_header *)STREAM_DATA(op->s);
|
||
|
|
||
|
if (oi->at_data.flags != 0) {
|
||
|
at_len = ospf6_auth_len_get(oi);
|
||
|
if (at_len) {
|
||
|
iovector[0].iov_len =
|
||
|
ntohs(oh->length) + at_len;
|
||
|
ospf6_auth_digest_send(oi->linklocal_addr, oi,
|
||
|
oh, at_len,
|
||
|
iovector[0].iov_len);
|
||
|
} else {
|
||
|
iovector[0].iov_len = ntohs(oh->length);
|
||
|
}
|
||
|
} else {
|
||
|
iovector[0].iov_len = ntohs(oh->length);
|
||
|
}
|
||
|
|
||
|
len = ospf6_sendmsg(oi->linklocal_addr, &op->dst,
|
||
|
oi->interface->ifindex, iovector,
|
||
|
ospf6->fd);
|
||
|
|
||
|
if (len != (op->length + (int)at_len))
|
||
|
flog_err(EC_LIB_DEVELOPMENT,
|
||
|
"Could not send entire message");
|
||
|
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND_HDR)) {
|
||
|
zlog_debug("%s send on %s",
|
||
|
ospf6_message_type(oh->type),
|
||
|
oi->interface->name);
|
||
|
zlog_debug(" src: %pI6", oi->linklocal_addr);
|
||
|
zlog_debug(" dst: %pI6", &op->dst);
|
||
|
switch (oh->type) {
|
||
|
case OSPF6_MESSAGE_TYPE_HELLO:
|
||
|
ospf6_hello_print(oh, OSPF6_ACTION_SEND);
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_DBDESC:
|
||
|
ospf6_dbdesc_print(oh, OSPF6_ACTION_SEND);
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_LSREQ:
|
||
|
ospf6_lsreq_print(oh, OSPF6_ACTION_SEND);
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_LSUPDATE:
|
||
|
ospf6_lsupdate_print(oh, OSPF6_ACTION_SEND);
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_LSACK:
|
||
|
ospf6_lsack_print(oh, OSPF6_ACTION_SEND);
|
||
|
break;
|
||
|
default:
|
||
|
zlog_debug("Unknown message");
|
||
|
assert(0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
switch (oh->type) {
|
||
|
case OSPF6_MESSAGE_TYPE_HELLO:
|
||
|
monotime(×tamp);
|
||
|
if (oi->hello_out)
|
||
|
latency = monotime_since(&oi->last_hello, NULL)
|
||
|
- ((int64_t)oi->hello_interval
|
||
|
* 1000000);
|
||
|
|
||
|
/* log if latency exceeds the hello period */
|
||
|
if (latency > ((int64_t)oi->hello_interval * 1000000))
|
||
|
zlog_warn("%s hello TX high latency %" PRId64
|
||
|
"us.",
|
||
|
__func__, latency);
|
||
|
oi->last_hello = timestamp;
|
||
|
oi->hello_out++;
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_DBDESC:
|
||
|
oi->db_desc_out++;
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_LSREQ:
|
||
|
oi->ls_req_out++;
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_LSUPDATE:
|
||
|
oi->ls_upd_out++;
|
||
|
break;
|
||
|
case OSPF6_MESSAGE_TYPE_LSACK:
|
||
|
oi->ls_ack_out++;
|
||
|
break;
|
||
|
default:
|
||
|
zlog_debug("Unknown message");
|
||
|
assert(0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((oi->at_data.flags != 0) &&
|
||
|
(IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND_HDR)) &&
|
||
|
(IS_OSPF6_DEBUG_AUTH_TX))
|
||
|
ospf6_auth_hdr_dump_send(oh, iovector[0].iov_len);
|
||
|
|
||
|
/* initialize at_len to 0 for next packet */
|
||
|
at_len = 0;
|
||
|
|
||
|
/* Now delete packet from queue. */
|
||
|
ospf6_packet_delete(oi);
|
||
|
|
||
|
/* Move this interface to the tail of write_q to
|
||
|
serve everyone in a round robin fashion */
|
||
|
list_delete_node(ospf6->oi_write_q, node);
|
||
|
if (ospf6_fifo_head(oi->obuf) == NULL) {
|
||
|
oi->on_write_q = 0;
|
||
|
oi = NULL;
|
||
|
} else {
|
||
|
listnode_add(ospf6->oi_write_q, oi);
|
||
|
}
|
||
|
|
||
|
/* Setup to service from the head of the queue again */
|
||
|
if (!list_isempty(ospf6->oi_write_q)) {
|
||
|
node = listhead(ospf6->oi_write_q);
|
||
|
oi = listgetdata(node);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* If packets still remain in queue, call write thread. */
|
||
|
if (!list_isempty(ospf6->oi_write_q))
|
||
|
event_add_write(master, ospf6_write, ospf6, ospf6->fd,
|
||
|
&ospf6->t_write);
|
||
|
}
|
||
|
|
||
|
void ospf6_hello_send(struct event *thread)
|
||
|
{
|
||
|
struct ospf6_interface *oi;
|
||
|
|
||
|
oi = (struct ospf6_interface *)EVENT_ARG(thread);
|
||
|
|
||
|
/* Check if the GR hello-delay is active. */
|
||
|
if (oi->gr.hello_delay.t_grace_send)
|
||
|
return;
|
||
|
|
||
|
/* Check if config is still being processed */
|
||
|
if (event_is_scheduled(t_ospf6_cfg)) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND))
|
||
|
zlog_debug(
|
||
|
"Suppressing Hello on interface %s during config load",
|
||
|
oi->interface->name);
|
||
|
event_add_timer(master, ospf6_hello_send, oi,
|
||
|
oi->hello_interval, &oi->thread_send_hello);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (oi->state <= OSPF6_INTERFACE_DOWN) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND_HDR))
|
||
|
zlog_debug("Unable to send Hello on down interface %s",
|
||
|
oi->interface->name);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
event_add_timer(master, ospf6_hello_send, oi, oi->hello_interval,
|
||
|
&oi->thread_send_hello);
|
||
|
|
||
|
ospf6_hello_send_addr(oi, NULL);
|
||
|
}
|
||
|
|
||
|
/* used to send polls for PtP/PtMP too */
|
||
|
void ospf6_hello_send_addr(struct ospf6_interface *oi,
|
||
|
const struct in6_addr *addr)
|
||
|
{
|
||
|
struct ospf6_packet *op;
|
||
|
uint16_t length = OSPF6_HEADER_SIZE;
|
||
|
bool anything = false;
|
||
|
|
||
|
op = ospf6_packet_new(oi->ifmtu);
|
||
|
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_HELLO, oi, op->s);
|
||
|
|
||
|
/* Prepare OSPF Hello body */
|
||
|
length += ospf6_make_hello(oi, op->s);
|
||
|
if (length == OSPF6_HEADER_SIZE) {
|
||
|
/* Hello overshooting MTU */
|
||
|
ospf6_packet_free(op);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Fill OSPF header. */
|
||
|
ospf6_fill_header(oi, op->s, length);
|
||
|
|
||
|
/* Set packet length. */
|
||
|
op->length = length;
|
||
|
|
||
|
if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT
|
||
|
|| oi->state == OSPF6_INTERFACE_POINTTOMULTIPOINT)
|
||
|
&& !addr && oi->p2xp_no_multicast_hello) {
|
||
|
struct listnode *node;
|
||
|
struct ospf6_neighbor *on;
|
||
|
struct ospf6_packet *opdup;
|
||
|
|
||
|
for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, node, on)) {
|
||
|
if (on->state < OSPF6_NEIGHBOR_INIT)
|
||
|
/* poll-interval for these */
|
||
|
continue;
|
||
|
|
||
|
opdup = ospf6_packet_dup(op);
|
||
|
opdup->dst = on->linklocal_addr;
|
||
|
ospf6_fill_hdr_checksum(oi, opdup);
|
||
|
ospf6_packet_add_top(oi, opdup);
|
||
|
anything = true;
|
||
|
}
|
||
|
|
||
|
ospf6_packet_free(op);
|
||
|
} else {
|
||
|
op->dst = addr ? *addr : allspfrouters6;
|
||
|
|
||
|
/* Add packet to the top of the interface output queue, so that
|
||
|
* they can't get delayed by things like long queues of LS
|
||
|
* Update packets
|
||
|
*/
|
||
|
ospf6_fill_hdr_checksum(oi, op);
|
||
|
ospf6_packet_add_top(oi, op);
|
||
|
anything = true;
|
||
|
}
|
||
|
|
||
|
if (anything)
|
||
|
OSPF6_MESSAGE_WRITE_ON(oi);
|
||
|
}
|
||
|
|
||
|
static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s)
|
||
|
{
|
||
|
uint16_t length = OSPF6_DB_DESC_MIN_SIZE;
|
||
|
struct ospf6_lsa *lsa, *lsanext;
|
||
|
uint8_t options1 = on->ospf6_if->area->options[1];
|
||
|
|
||
|
if (on->ospf6_if->at_data.flags != 0)
|
||
|
options1 |= OSPF6_OPT_AT;
|
||
|
|
||
|
/* if this is initial one, initialize sequence number for DbDesc */
|
||
|
if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)
|
||
|
&& (on->dbdesc_seqnum == 0)) {
|
||
|
on->dbdesc_seqnum = frr_sequence32_next();
|
||
|
}
|
||
|
|
||
|
/* reserved */
|
||
|
stream_putc(s, 0); /* reserved 1 */
|
||
|
stream_putc(s, on->ospf6_if->area->options[0]);
|
||
|
stream_putc(s, options1);
|
||
|
stream_putc(s, on->ospf6_if->area->options[2]);
|
||
|
stream_putw(s, on->ospf6_if->ifmtu);
|
||
|
stream_putc(s, 0); /* reserved 2 */
|
||
|
stream_putc(s, on->dbdesc_bits);
|
||
|
stream_putl(s, on->dbdesc_seqnum);
|
||
|
|
||
|
/* if this is not initial one, set LSA headers in dbdesc */
|
||
|
if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)) {
|
||
|
for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) {
|
||
|
ospf6_lsa_age_update_to_send(lsa,
|
||
|
on->ospf6_if->transdelay);
|
||
|
|
||
|
/* MTU check */
|
||
|
if ((length + sizeof(struct ospf6_lsa_header)
|
||
|
+ OSPF6_HEADER_SIZE)
|
||
|
> ospf6_packet_max(on->ospf6_if)) {
|
||
|
ospf6_lsa_unlock(&lsa);
|
||
|
if (lsanext)
|
||
|
ospf6_lsa_unlock(&lsanext);
|
||
|
break;
|
||
|
}
|
||
|
stream_put(s, lsa->header,
|
||
|
sizeof(struct ospf6_lsa_header));
|
||
|
length += sizeof(struct ospf6_lsa_header);
|
||
|
}
|
||
|
}
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
void ospf6_dbdesc_send(struct event *thread)
|
||
|
{
|
||
|
struct ospf6_neighbor *on;
|
||
|
uint16_t length = OSPF6_HEADER_SIZE;
|
||
|
struct ospf6_packet *op;
|
||
|
|
||
|
on = (struct ospf6_neighbor *)EVENT_ARG(thread);
|
||
|
|
||
|
if (on->state < OSPF6_NEIGHBOR_EXSTART) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND))
|
||
|
zlog_debug(
|
||
|
"Quit to send DbDesc to neighbor %s state %s",
|
||
|
on->name, ospf6_neighbor_state_str[on->state]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* set next thread if master */
|
||
|
if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT))
|
||
|
event_add_timer(master, ospf6_dbdesc_send, on,
|
||
|
on->ospf6_if->rxmt_interval,
|
||
|
&on->thread_send_dbdesc);
|
||
|
|
||
|
op = ospf6_packet_new(on->ospf6_if->ifmtu);
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_DBDESC, on->ospf6_if, op->s);
|
||
|
|
||
|
length += ospf6_make_dbdesc(on, op->s);
|
||
|
ospf6_fill_header(on->ospf6_if, op->s, length);
|
||
|
|
||
|
/* Set packet length. */
|
||
|
op->length = length;
|
||
|
|
||
|
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
|
||
|
op->dst = allspfrouters6;
|
||
|
else
|
||
|
op->dst = on->linklocal_addr;
|
||
|
|
||
|
ospf6_fill_hdr_checksum(on->ospf6_if, op);
|
||
|
|
||
|
ospf6_packet_add(on->ospf6_if, op);
|
||
|
|
||
|
OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
|
||
|
}
|
||
|
|
||
|
void ospf6_dbdesc_send_newone(struct event *thread)
|
||
|
{
|
||
|
struct ospf6_neighbor *on;
|
||
|
struct ospf6_lsa *lsa, *lsanext;
|
||
|
unsigned int size = 0;
|
||
|
|
||
|
on = (struct ospf6_neighbor *)EVENT_ARG(thread);
|
||
|
ospf6_lsdb_remove_all(on->dbdesc_list);
|
||
|
|
||
|
/* move LSAs from summary_list to dbdesc_list (within neighbor
|
||
|
structure)
|
||
|
so that ospf6_send_dbdesc () can send those LSAs */
|
||
|
size = sizeof(struct ospf6_lsa_header) + sizeof(struct ospf6_dbdesc);
|
||
|
for (ALL_LSDB(on->summary_list, lsa, lsanext)) {
|
||
|
/* if stub area then don't advertise AS-External LSAs */
|
||
|
if ((IS_AREA_STUB(on->ospf6_if->area)
|
||
|
|| IS_AREA_NSSA(on->ospf6_if->area))
|
||
|
&& ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) {
|
||
|
ospf6_lsdb_remove(lsa, on->summary_list);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (size + sizeof(struct ospf6_lsa_header)
|
||
|
> ospf6_packet_max(on->ospf6_if)) {
|
||
|
ospf6_lsa_unlock(&lsa);
|
||
|
if (lsanext)
|
||
|
ospf6_lsa_unlock(&lsanext);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->dbdesc_list);
|
||
|
ospf6_lsdb_remove(lsa, on->summary_list);
|
||
|
size += sizeof(struct ospf6_lsa_header);
|
||
|
}
|
||
|
|
||
|
if (on->summary_list->count == 0)
|
||
|
UNSET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT);
|
||
|
|
||
|
/* If slave, More bit check must be done here */
|
||
|
if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT) && /* Slave */
|
||
|
!CHECK_FLAG(on->dbdesc_last.bits, OSPF6_DBDESC_MBIT)
|
||
|
&& !CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT))
|
||
|
event_add_event(master, exchange_done, on, 0,
|
||
|
&on->thread_exchange_done);
|
||
|
|
||
|
event_execute(master, ospf6_dbdesc_send, on, 0, NULL);
|
||
|
}
|
||
|
|
||
|
static uint16_t ospf6_make_lsreq(struct ospf6_neighbor *on, struct stream *s)
|
||
|
{
|
||
|
uint16_t length = 0;
|
||
|
struct ospf6_lsa *lsa, *lsanext, *last_req = NULL;
|
||
|
|
||
|
for (ALL_LSDB(on->request_list, lsa, lsanext)) {
|
||
|
if ((length + OSPF6_HEADER_SIZE)
|
||
|
> ospf6_packet_max(on->ospf6_if)) {
|
||
|
ospf6_lsa_unlock(&lsa);
|
||
|
if (lsanext)
|
||
|
ospf6_lsa_unlock(&lsanext);
|
||
|
break;
|
||
|
}
|
||
|
stream_putw(s, 0); /* reserved */
|
||
|
stream_putw(s, ntohs(lsa->header->type));
|
||
|
stream_putl(s, ntohl(lsa->header->id));
|
||
|
stream_putl(s, ntohl(lsa->header->adv_router));
|
||
|
length += sizeof(struct ospf6_lsreq_entry);
|
||
|
last_req = lsa;
|
||
|
}
|
||
|
|
||
|
if (last_req != NULL) {
|
||
|
if (on->last_ls_req != NULL)
|
||
|
ospf6_lsa_unlock(&on->last_ls_req);
|
||
|
|
||
|
ospf6_lsa_lock(last_req);
|
||
|
on->last_ls_req = last_req;
|
||
|
}
|
||
|
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
static uint16_t ospf6_make_lsack_neighbor(struct ospf6_neighbor *on,
|
||
|
struct ospf6_packet **op)
|
||
|
{
|
||
|
uint16_t length = 0;
|
||
|
struct ospf6_lsa *lsa, *lsanext;
|
||
|
int lsa_cnt = 0;
|
||
|
|
||
|
for (ALL_LSDB(on->lsack_list, lsa, lsanext)) {
|
||
|
if ((length + sizeof(struct ospf6_lsa_header)
|
||
|
+ OSPF6_HEADER_SIZE)
|
||
|
> ospf6_packet_max(on->ospf6_if)) {
|
||
|
/* if we run out of packet size/space here,
|
||
|
better to try again soon. */
|
||
|
if (lsa_cnt) {
|
||
|
ospf6_fill_header(on->ospf6_if, (*op)->s,
|
||
|
length + OSPF6_HEADER_SIZE);
|
||
|
|
||
|
(*op)->length = length + OSPF6_HEADER_SIZE;
|
||
|
(*op)->dst = on->linklocal_addr;
|
||
|
ospf6_fill_hdr_checksum(on->ospf6_if, *op);
|
||
|
ospf6_packet_add(on->ospf6_if, *op);
|
||
|
OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
|
||
|
/* new packet */
|
||
|
*op = ospf6_packet_new(on->ospf6_if->ifmtu);
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK,
|
||
|
on->ospf6_if, (*op)->s);
|
||
|
length = 0;
|
||
|
lsa_cnt = 0;
|
||
|
}
|
||
|
}
|
||
|
ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
|
||
|
stream_put((*op)->s, lsa->header,
|
||
|
sizeof(struct ospf6_lsa_header));
|
||
|
length += sizeof(struct ospf6_lsa_header);
|
||
|
|
||
|
assert(lsa->lock == 2);
|
||
|
ospf6_lsdb_remove(lsa, on->lsack_list);
|
||
|
lsa_cnt++;
|
||
|
}
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
void ospf6_lsreq_send(struct event *thread)
|
||
|
{
|
||
|
struct ospf6_neighbor *on;
|
||
|
struct ospf6_packet *op;
|
||
|
uint16_t length = OSPF6_HEADER_SIZE;
|
||
|
|
||
|
on = (struct ospf6_neighbor *)EVENT_ARG(thread);
|
||
|
|
||
|
/* LSReq will be sent only in ExStart or Loading */
|
||
|
if (on->state != OSPF6_NEIGHBOR_EXCHANGE
|
||
|
&& on->state != OSPF6_NEIGHBOR_LOADING) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSREQ, SEND_HDR))
|
||
|
zlog_debug("Quit to send LSReq to neighbor %s state %s",
|
||
|
on->name,
|
||
|
ospf6_neighbor_state_str[on->state]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* schedule loading_done if request list is empty */
|
||
|
if (on->request_list->count == 0) {
|
||
|
event_add_event(master, loading_done, on, 0,
|
||
|
&on->event_loading_done);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
op = ospf6_packet_new(on->ospf6_if->ifmtu);
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_LSREQ, on->ospf6_if, op->s);
|
||
|
|
||
|
length += ospf6_make_lsreq(on, op->s);
|
||
|
|
||
|
if (length == OSPF6_HEADER_SIZE) {
|
||
|
/* Hello overshooting MTU */
|
||
|
ospf6_packet_free(op);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Fill OSPF header. */
|
||
|
ospf6_fill_header(on->ospf6_if, op->s, length);
|
||
|
|
||
|
/* Set packet length */
|
||
|
op->length = length;
|
||
|
|
||
|
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
|
||
|
op->dst = allspfrouters6;
|
||
|
else
|
||
|
op->dst = on->linklocal_addr;
|
||
|
|
||
|
ospf6_fill_hdr_checksum(on->ospf6_if, op);
|
||
|
ospf6_packet_add(on->ospf6_if, op);
|
||
|
|
||
|
OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
|
||
|
|
||
|
/* set next thread */
|
||
|
if (on->request_list->count != 0) {
|
||
|
event_add_timer(master, ospf6_lsreq_send, on,
|
||
|
on->ospf6_if->rxmt_interval,
|
||
|
&on->thread_send_lsreq);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void ospf6_send_lsupdate(struct ospf6_neighbor *on,
|
||
|
struct ospf6_interface *oi,
|
||
|
struct ospf6_packet *op)
|
||
|
{
|
||
|
if (on) {
|
||
|
if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
|
||
|
|| (on->ospf6_if->state == OSPF6_INTERFACE_DR)
|
||
|
|| (on->ospf6_if->state == OSPF6_INTERFACE_BDR))
|
||
|
op->dst = allspfrouters6;
|
||
|
else
|
||
|
op->dst = on->linklocal_addr;
|
||
|
oi = on->ospf6_if;
|
||
|
} else if (oi) {
|
||
|
if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT)
|
||
|
|| (oi->state == OSPF6_INTERFACE_DR)
|
||
|
|| (oi->state == OSPF6_INTERFACE_BDR))
|
||
|
op->dst = allspfrouters6;
|
||
|
else
|
||
|
op->dst = alldrouters6;
|
||
|
}
|
||
|
if (oi) {
|
||
|
struct ospf6 *ospf6;
|
||
|
|
||
|
ospf6_fill_hdr_checksum(oi, op);
|
||
|
ospf6_packet_add(oi, op);
|
||
|
/* If ospf instance is being deleted, send the packet
|
||
|
* immediately
|
||
|
*/
|
||
|
if ((oi->area == NULL) || (oi->area->ospf6 == NULL))
|
||
|
return;
|
||
|
|
||
|
ospf6 = oi->area->ospf6;
|
||
|
if (ospf6->inst_shutdown) {
|
||
|
if (oi->on_write_q == 0) {
|
||
|
listnode_add(ospf6->oi_write_q, oi);
|
||
|
oi->on_write_q = 1;
|
||
|
}
|
||
|
/*
|
||
|
* When ospf6d immediately calls event_execute
|
||
|
* for items in the oi_write_q. The event_execute
|
||
|
* will call ospf6_write and cause the oi_write_q
|
||
|
* to be emptied. *IF* there is already an event
|
||
|
* scheduled for the oi_write_q by something else
|
||
|
* then when it wakes up in the future and attempts
|
||
|
* to cycle through items in the queue it will
|
||
|
* assert. Let's stop the t_write event and
|
||
|
* if ospf6_write doesn't finish up the work
|
||
|
* it will schedule itself again.
|
||
|
*/
|
||
|
event_cancel(&ospf6->t_write);
|
||
|
event_execute(master, ospf6_write, ospf6, 0, NULL);
|
||
|
} else
|
||
|
OSPF6_MESSAGE_WRITE_ON(oi);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static uint16_t ospf6_make_lsupdate_list(struct ospf6_neighbor *on,
|
||
|
struct ospf6_packet **op, int *lsa_cnt)
|
||
|
{
|
||
|
uint16_t length = OSPF6_LS_UPD_MIN_SIZE;
|
||
|
struct ospf6_lsa *lsa, *lsanext;
|
||
|
|
||
|
/* skip over fixed header */
|
||
|
stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
|
||
|
|
||
|
for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) {
|
||
|
if ((length + ospf6_lsa_size(lsa->header) + OSPF6_HEADER_SIZE) >
|
||
|
ospf6_packet_max(on->ospf6_if)) {
|
||
|
ospf6_fill_header(on->ospf6_if, (*op)->s,
|
||
|
length + OSPF6_HEADER_SIZE);
|
||
|
(*op)->length = length + OSPF6_HEADER_SIZE;
|
||
|
ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt);
|
||
|
ospf6_send_lsupdate(on, NULL, *op);
|
||
|
|
||
|
/* refresh packet */
|
||
|
*op = ospf6_packet_new(on->ospf6_if->ifmtu);
|
||
|
length = OSPF6_LS_UPD_MIN_SIZE;
|
||
|
*lsa_cnt = 0;
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE,
|
||
|
on->ospf6_if, (*op)->s);
|
||
|
stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
|
||
|
}
|
||
|
ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
|
||
|
stream_put((*op)->s, lsa->header, ospf6_lsa_size(lsa->header));
|
||
|
(*lsa_cnt)++;
|
||
|
length += ospf6_lsa_size(lsa->header);
|
||
|
assert(lsa->lock == 2);
|
||
|
ospf6_lsdb_remove(lsa, on->lsupdate_list);
|
||
|
}
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
static uint16_t ospf6_make_ls_retrans_list(struct ospf6_neighbor *on,
|
||
|
struct ospf6_packet **op,
|
||
|
int *lsa_cnt)
|
||
|
{
|
||
|
uint16_t length = OSPF6_LS_UPD_MIN_SIZE;
|
||
|
struct ospf6_lsa *lsa, *lsanext;
|
||
|
|
||
|
/* skip over fixed header */
|
||
|
stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
|
||
|
|
||
|
for (ALL_LSDB(on->retrans_list, lsa, lsanext)) {
|
||
|
if ((length + ospf6_lsa_size(lsa->header) + OSPF6_HEADER_SIZE) >
|
||
|
ospf6_packet_max(on->ospf6_if)) {
|
||
|
ospf6_fill_header(on->ospf6_if, (*op)->s,
|
||
|
length + OSPF6_HEADER_SIZE);
|
||
|
(*op)->length = length + OSPF6_HEADER_SIZE;
|
||
|
ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt);
|
||
|
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
|
||
|
(*op)->dst = allspfrouters6;
|
||
|
else
|
||
|
(*op)->dst = on->linklocal_addr;
|
||
|
|
||
|
ospf6_fill_hdr_checksum(on->ospf6_if, *op);
|
||
|
ospf6_packet_add(on->ospf6_if, *op);
|
||
|
OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
|
||
|
|
||
|
/* refresh packet */
|
||
|
*op = ospf6_packet_new(on->ospf6_if->ifmtu);
|
||
|
length = OSPF6_LS_UPD_MIN_SIZE;
|
||
|
*lsa_cnt = 0;
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE,
|
||
|
on->ospf6_if, (*op)->s);
|
||
|
stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
|
||
|
}
|
||
|
ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
|
||
|
stream_put((*op)->s, lsa->header, ospf6_lsa_size(lsa->header));
|
||
|
(*lsa_cnt)++;
|
||
|
length += ospf6_lsa_size(lsa->header);
|
||
|
}
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
void ospf6_lsupdate_send_neighbor(struct event *thread)
|
||
|
{
|
||
|
struct ospf6_neighbor *on;
|
||
|
struct ospf6_packet *op;
|
||
|
uint16_t length = OSPF6_HEADER_SIZE;
|
||
|
int lsa_cnt = 0;
|
||
|
|
||
|
on = (struct ospf6_neighbor *)EVENT_ARG(thread);
|
||
|
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND_HDR))
|
||
|
zlog_debug("LSUpdate to neighbor %s", on->name);
|
||
|
|
||
|
if (on->state < OSPF6_NEIGHBOR_EXCHANGE) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE,
|
||
|
SEND_HDR))
|
||
|
zlog_debug("Quit to send (neighbor state %s)",
|
||
|
ospf6_neighbor_state_str[on->state]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* first do lsupdate_list */
|
||
|
op = ospf6_packet_new(on->ospf6_if->ifmtu);
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s);
|
||
|
length += ospf6_make_lsupdate_list(on, &op, &lsa_cnt);
|
||
|
if (lsa_cnt) {
|
||
|
/* Fill OSPF header. */
|
||
|
ospf6_fill_header(on->ospf6_if, op->s, length);
|
||
|
ospf6_fill_lsupdate_header(op->s, lsa_cnt);
|
||
|
op->length = length;
|
||
|
ospf6_send_lsupdate(on, NULL, op);
|
||
|
|
||
|
/* prepare new packet */
|
||
|
op = ospf6_packet_new(on->ospf6_if->ifmtu);
|
||
|
length = OSPF6_HEADER_SIZE;
|
||
|
lsa_cnt = 0;
|
||
|
} else {
|
||
|
stream_reset(op->s);
|
||
|
length = OSPF6_HEADER_SIZE;
|
||
|
}
|
||
|
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s);
|
||
|
/* now do retransmit list */
|
||
|
length += ospf6_make_ls_retrans_list(on, &op, &lsa_cnt);
|
||
|
if (lsa_cnt) {
|
||
|
ospf6_fill_header(on->ospf6_if, op->s, length);
|
||
|
ospf6_fill_lsupdate_header(op->s, lsa_cnt);
|
||
|
op->length = length;
|
||
|
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
|
||
|
op->dst = allspfrouters6;
|
||
|
else
|
||
|
op->dst = on->linklocal_addr;
|
||
|
ospf6_fill_hdr_checksum(on->ospf6_if, op);
|
||
|
ospf6_packet_add(on->ospf6_if, op);
|
||
|
OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
|
||
|
} else
|
||
|
ospf6_packet_free(op);
|
||
|
|
||
|
if (on->lsupdate_list->count != 0) {
|
||
|
event_add_event(master, ospf6_lsupdate_send_neighbor, on, 0,
|
||
|
&on->thread_send_lsupdate);
|
||
|
} else if (on->retrans_list->count != 0) {
|
||
|
event_add_timer(master, ospf6_lsupdate_send_neighbor, on,
|
||
|
on->ospf6_if->rxmt_interval,
|
||
|
&on->thread_send_lsupdate);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on,
|
||
|
struct ospf6_lsa *lsa)
|
||
|
{
|
||
|
struct ospf6_packet *op;
|
||
|
uint16_t length = OSPF6_HEADER_SIZE;
|
||
|
|
||
|
op = ospf6_packet_new(on->ospf6_if->ifmtu);
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s);
|
||
|
|
||
|
/* skip over fixed header */
|
||
|
stream_forward_endp(op->s, OSPF6_LS_UPD_MIN_SIZE);
|
||
|
ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
|
||
|
stream_put(op->s, lsa->header, ospf6_lsa_size(lsa->header));
|
||
|
length = OSPF6_HEADER_SIZE + OSPF6_LS_UPD_MIN_SIZE +
|
||
|
ospf6_lsa_size(lsa->header);
|
||
|
ospf6_fill_header(on->ospf6_if, op->s, length);
|
||
|
ospf6_fill_lsupdate_header(op->s, 1);
|
||
|
op->length = length;
|
||
|
|
||
|
if (IS_OSPF6_DEBUG_FLOODING
|
||
|
|| IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND_HDR))
|
||
|
zlog_debug("%s: Send lsupdate with lsa %s (age %u)", __func__,
|
||
|
lsa->name, ntohs(lsa->header->age));
|
||
|
|
||
|
ospf6_send_lsupdate(on, NULL, op);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static uint16_t ospf6_make_lsupdate_interface(struct ospf6_interface *oi,
|
||
|
struct ospf6_packet **op,
|
||
|
int *lsa_cnt)
|
||
|
{
|
||
|
uint16_t length = OSPF6_LS_UPD_MIN_SIZE;
|
||
|
struct ospf6_lsa *lsa, *lsanext;
|
||
|
|
||
|
/* skip over fixed header */
|
||
|
stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
|
||
|
|
||
|
for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) {
|
||
|
if (length + ospf6_lsa_size(lsa->header) + OSPF6_HEADER_SIZE >
|
||
|
ospf6_packet_max(oi)) {
|
||
|
ospf6_fill_header(oi, (*op)->s,
|
||
|
length + OSPF6_HEADER_SIZE);
|
||
|
(*op)->length = length + OSPF6_HEADER_SIZE;
|
||
|
ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt);
|
||
|
ospf6_send_lsupdate(NULL, oi, *op);
|
||
|
|
||
|
/* refresh packet */
|
||
|
*op = ospf6_packet_new(oi->ifmtu);
|
||
|
length = OSPF6_LS_UPD_MIN_SIZE;
|
||
|
*lsa_cnt = 0;
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi,
|
||
|
(*op)->s);
|
||
|
stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
|
||
|
}
|
||
|
|
||
|
ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
|
||
|
stream_put((*op)->s, lsa->header, ospf6_lsa_size(lsa->header));
|
||
|
(*lsa_cnt)++;
|
||
|
length += ospf6_lsa_size(lsa->header);
|
||
|
|
||
|
assert(lsa->lock == 2);
|
||
|
ospf6_lsdb_remove(lsa, oi->lsupdate_list);
|
||
|
}
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
void ospf6_lsupdate_send_interface(struct event *thread)
|
||
|
{
|
||
|
struct ospf6_interface *oi;
|
||
|
struct ospf6_packet *op;
|
||
|
uint16_t length = OSPF6_HEADER_SIZE;
|
||
|
int lsa_cnt = 0;
|
||
|
|
||
|
oi = (struct ospf6_interface *)EVENT_ARG(thread);
|
||
|
|
||
|
if (oi->state <= OSPF6_INTERFACE_WAITING) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE,
|
||
|
SEND_HDR))
|
||
|
zlog_debug(
|
||
|
"Quit to send LSUpdate to interface %s state %s",
|
||
|
oi->interface->name,
|
||
|
ospf6_interface_state_str[oi->state]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* if we have nothing to send, return */
|
||
|
if (oi->lsupdate_list->count == 0)
|
||
|
return;
|
||
|
|
||
|
op = ospf6_packet_new(oi->ifmtu);
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi, op->s);
|
||
|
length += ospf6_make_lsupdate_interface(oi, &op, &lsa_cnt);
|
||
|
if (lsa_cnt) {
|
||
|
/* Fill OSPF header. */
|
||
|
ospf6_fill_header(oi, op->s, length);
|
||
|
ospf6_fill_lsupdate_header(op->s, lsa_cnt);
|
||
|
op->length = length;
|
||
|
ospf6_send_lsupdate(NULL, oi, op);
|
||
|
} else
|
||
|
ospf6_packet_free(op);
|
||
|
|
||
|
if (oi->lsupdate_list->count > 0) {
|
||
|
event_add_event(master, ospf6_lsupdate_send_interface, oi, 0,
|
||
|
&oi->thread_send_lsupdate);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ospf6_lsack_send_neighbor(struct event *thread)
|
||
|
{
|
||
|
struct ospf6_neighbor *on;
|
||
|
struct ospf6_packet *op;
|
||
|
uint16_t length = OSPF6_HEADER_SIZE;
|
||
|
|
||
|
on = (struct ospf6_neighbor *)EVENT_ARG(thread);
|
||
|
|
||
|
if (on->state < OSPF6_NEIGHBOR_EXCHANGE) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSACK, SEND_HDR))
|
||
|
zlog_debug("Quit to send LSAck to neighbor %s state %s",
|
||
|
on->name,
|
||
|
ospf6_neighbor_state_str[on->state]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* if we have nothing to send, return */
|
||
|
if (on->lsack_list->count == 0)
|
||
|
return;
|
||
|
|
||
|
op = ospf6_packet_new(on->ospf6_if->ifmtu);
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, on->ospf6_if, op->s);
|
||
|
|
||
|
length += ospf6_make_lsack_neighbor(on, &op);
|
||
|
|
||
|
if (length == OSPF6_HEADER_SIZE) {
|
||
|
ospf6_packet_free(op);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Fill OSPF header. */
|
||
|
ospf6_fill_header(on->ospf6_if, op->s, length);
|
||
|
|
||
|
/* Set packet length, dst and queue to FIFO. */
|
||
|
op->length = length;
|
||
|
op->dst = on->linklocal_addr;
|
||
|
ospf6_fill_hdr_checksum(on->ospf6_if, op);
|
||
|
ospf6_packet_add(on->ospf6_if, op);
|
||
|
OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
|
||
|
|
||
|
if (on->lsack_list->count > 0)
|
||
|
event_add_event(master, ospf6_lsack_send_neighbor, on, 0,
|
||
|
&on->thread_send_lsack);
|
||
|
}
|
||
|
|
||
|
static uint16_t ospf6_make_lsack_interface(struct ospf6_interface *oi,
|
||
|
struct ospf6_packet *op)
|
||
|
{
|
||
|
uint16_t length = 0;
|
||
|
struct ospf6_lsa *lsa, *lsanext;
|
||
|
|
||
|
for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) {
|
||
|
if ((length + sizeof(struct ospf6_lsa_header)
|
||
|
+ OSPF6_HEADER_SIZE)
|
||
|
> ospf6_packet_max(oi)) {
|
||
|
/* if we run out of packet size/space here,
|
||
|
better to try again soon. */
|
||
|
EVENT_OFF(oi->thread_send_lsack);
|
||
|
event_add_event(master, ospf6_lsack_send_interface, oi,
|
||
|
0, &oi->thread_send_lsack);
|
||
|
|
||
|
ospf6_lsa_unlock(&lsa);
|
||
|
if (lsanext)
|
||
|
ospf6_lsa_unlock(&lsanext);
|
||
|
break;
|
||
|
}
|
||
|
ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
|
||
|
stream_put(op->s, lsa->header, sizeof(struct ospf6_lsa_header));
|
||
|
length += sizeof(struct ospf6_lsa_header);
|
||
|
|
||
|
assert(lsa->lock == 2);
|
||
|
ospf6_lsdb_remove(lsa, oi->lsack_list);
|
||
|
}
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
void ospf6_lsack_send_interface(struct event *thread)
|
||
|
{
|
||
|
struct ospf6_interface *oi;
|
||
|
struct ospf6_packet *op;
|
||
|
uint16_t length = OSPF6_HEADER_SIZE;
|
||
|
|
||
|
oi = (struct ospf6_interface *)EVENT_ARG(thread);
|
||
|
|
||
|
if (oi->state <= OSPF6_INTERFACE_WAITING) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSACK, SEND_HDR))
|
||
|
zlog_debug(
|
||
|
"Quit to send LSAck to interface %s state %s",
|
||
|
oi->interface->name,
|
||
|
ospf6_interface_state_str[oi->state]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* if we have nothing to send, return */
|
||
|
if (oi->lsack_list->count == 0)
|
||
|
return;
|
||
|
|
||
|
op = ospf6_packet_new(oi->ifmtu);
|
||
|
ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, oi, op->s);
|
||
|
|
||
|
length += ospf6_make_lsack_interface(oi, op);
|
||
|
|
||
|
if (length == OSPF6_HEADER_SIZE) {
|
||
|
ospf6_packet_free(op);
|
||
|
return;
|
||
|
}
|
||
|
/* Fill OSPF header. */
|
||
|
ospf6_fill_header(oi, op->s, length);
|
||
|
|
||
|
/* Set packet length, dst and queue to FIFO. */
|
||
|
op->length = length;
|
||
|
if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT)
|
||
|
|| (oi->state == OSPF6_INTERFACE_DR)
|
||
|
|| (oi->state == OSPF6_INTERFACE_BDR))
|
||
|
op->dst = allspfrouters6;
|
||
|
else
|
||
|
op->dst = alldrouters6;
|
||
|
|
||
|
ospf6_fill_hdr_checksum(oi, op);
|
||
|
ospf6_packet_add(oi, op);
|
||
|
OSPF6_MESSAGE_WRITE_ON(oi);
|
||
|
|
||
|
if (oi->lsack_list->count > 0)
|
||
|
event_add_event(master, ospf6_lsack_send_interface, oi, 0,
|
||
|
&oi->thread_send_lsack);
|
||
|
}
|
||
|
|
||
|
/* Commands */
|
||
|
DEFUN(debug_ospf6_message, debug_ospf6_message_cmd,
|
||
|
"debug ospf6 message <unknown|hello|dbdesc|lsreq|lsupdate|lsack|all> [<send|recv|send-hdr|recv-hdr>]",
|
||
|
DEBUG_STR OSPF6_STR
|
||
|
"Debug OSPFv3 message\n"
|
||
|
"Debug Unknown message\n"
|
||
|
"Debug Hello message\n"
|
||
|
"Debug Database Description message\n"
|
||
|
"Debug Link State Request message\n"
|
||
|
"Debug Link State Update message\n"
|
||
|
"Debug Link State Acknowledgement message\n"
|
||
|
"Debug All message\n"
|
||
|
"Debug only sending message, entire packet\n"
|
||
|
"Debug only receiving message, entire packet\n"
|
||
|
"Debug only sending message, header only\n"
|
||
|
"Debug only receiving message, header only\n")
|
||
|
{
|
||
|
int idx_packet = 3;
|
||
|
int idx_send_recv = 4;
|
||
|
unsigned char level = 0;
|
||
|
int type = 0;
|
||
|
int i;
|
||
|
|
||
|
/* check type */
|
||
|
if (!strncmp(argv[idx_packet]->arg, "u", 1))
|
||
|
type = OSPF6_MESSAGE_TYPE_UNKNOWN;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "h", 1))
|
||
|
type = OSPF6_MESSAGE_TYPE_HELLO;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "d", 1))
|
||
|
type = OSPF6_MESSAGE_TYPE_DBDESC;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "lsr", 3))
|
||
|
type = OSPF6_MESSAGE_TYPE_LSREQ;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "lsu", 3))
|
||
|
type = OSPF6_MESSAGE_TYPE_LSUPDATE;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "lsa", 3))
|
||
|
type = OSPF6_MESSAGE_TYPE_LSACK;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "a", 1))
|
||
|
type = OSPF6_MESSAGE_TYPE_ALL;
|
||
|
|
||
|
if (argc == 4)
|
||
|
level = OSPF6_DEBUG_MESSAGE_SEND | OSPF6_DEBUG_MESSAGE_RECV;
|
||
|
else if (!strncmp(argv[idx_send_recv]->arg, "send-h", 6))
|
||
|
level = OSPF6_DEBUG_MESSAGE_SEND_HDR;
|
||
|
else if (!strncmp(argv[idx_send_recv]->arg, "s", 1))
|
||
|
level = OSPF6_DEBUG_MESSAGE_SEND;
|
||
|
else if (!strncmp(argv[idx_send_recv]->arg, "recv-h", 6))
|
||
|
level = OSPF6_DEBUG_MESSAGE_RECV_HDR;
|
||
|
else if (!strncmp(argv[idx_send_recv]->arg, "r", 1))
|
||
|
level = OSPF6_DEBUG_MESSAGE_RECV;
|
||
|
|
||
|
if (type == OSPF6_MESSAGE_TYPE_ALL) {
|
||
|
for (i = 0; i < 6; i++)
|
||
|
OSPF6_DEBUG_MESSAGE_ON(i, level);
|
||
|
} else
|
||
|
OSPF6_DEBUG_MESSAGE_ON(type, level);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
DEFUN(no_debug_ospf6_message, no_debug_ospf6_message_cmd,
|
||
|
"no debug ospf6 message <unknown|hello|dbdesc|lsreq|lsupdate|lsack|all> [<send|recv|send-hdr|recv-hdr>]",
|
||
|
NO_STR DEBUG_STR OSPF6_STR
|
||
|
"Debug OSPFv3 message\n"
|
||
|
"Debug Unknown message\n"
|
||
|
"Debug Hello message\n"
|
||
|
"Debug Database Description message\n"
|
||
|
"Debug Link State Request message\n"
|
||
|
"Debug Link State Update message\n"
|
||
|
"Debug Link State Acknowledgement message\n"
|
||
|
"Debug All message\n"
|
||
|
"Debug only sending message, entire pkt\n"
|
||
|
"Debug only receiving message, entire pkt\n"
|
||
|
"Debug only sending message, header only\n"
|
||
|
"Debug only receiving message, header only\n")
|
||
|
{
|
||
|
int idx_packet = 4;
|
||
|
int idx_send_recv = 5;
|
||
|
unsigned char level = 0;
|
||
|
int type = 0;
|
||
|
int i;
|
||
|
|
||
|
/* check type */
|
||
|
if (!strncmp(argv[idx_packet]->arg, "u", 1))
|
||
|
type = OSPF6_MESSAGE_TYPE_UNKNOWN;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "h", 1))
|
||
|
type = OSPF6_MESSAGE_TYPE_HELLO;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "d", 1))
|
||
|
type = OSPF6_MESSAGE_TYPE_DBDESC;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "lsr", 3))
|
||
|
type = OSPF6_MESSAGE_TYPE_LSREQ;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "lsu", 3))
|
||
|
type = OSPF6_MESSAGE_TYPE_LSUPDATE;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "lsa", 3))
|
||
|
type = OSPF6_MESSAGE_TYPE_LSACK;
|
||
|
else if (!strncmp(argv[idx_packet]->arg, "a", 1))
|
||
|
type = OSPF6_MESSAGE_TYPE_ALL;
|
||
|
|
||
|
if (argc == 5)
|
||
|
level = OSPF6_DEBUG_MESSAGE_SEND | OSPF6_DEBUG_MESSAGE_RECV
|
||
|
| OSPF6_DEBUG_MESSAGE_SEND_HDR
|
||
|
| OSPF6_DEBUG_MESSAGE_RECV_HDR;
|
||
|
else if (!strncmp(argv[idx_send_recv]->arg, "send-h", 6))
|
||
|
level = OSPF6_DEBUG_MESSAGE_SEND_HDR;
|
||
|
else if (!strncmp(argv[idx_send_recv]->arg, "s", 1))
|
||
|
level = OSPF6_DEBUG_MESSAGE_SEND;
|
||
|
else if (!strncmp(argv[idx_send_recv]->arg, "recv-h", 6))
|
||
|
level = OSPF6_DEBUG_MESSAGE_RECV_HDR;
|
||
|
else if (!strncmp(argv[idx_send_recv]->arg, "r", 1))
|
||
|
level = OSPF6_DEBUG_MESSAGE_RECV;
|
||
|
|
||
|
if (type == OSPF6_MESSAGE_TYPE_ALL) {
|
||
|
for (i = 0; i < 6; i++)
|
||
|
OSPF6_DEBUG_MESSAGE_OFF(i, level);
|
||
|
} else
|
||
|
OSPF6_DEBUG_MESSAGE_OFF(type, level);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
int config_write_ospf6_debug_message(struct vty *vty)
|
||
|
{
|
||
|
const char *type_str[] = {"unknown", "hello", "dbdesc",
|
||
|
"lsreq", "lsupdate", "lsack"};
|
||
|
unsigned char s = 0, r = 0, sh = 0, rh = 0;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < 6; i++) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, SEND))
|
||
|
s |= 1 << i;
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, RECV))
|
||
|
r |= 1 << i;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 6; i++) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, SEND_HDR))
|
||
|
sh |= 1 << i;
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, RECV_HDR))
|
||
|
rh |= 1 << i;
|
||
|
}
|
||
|
|
||
|
if (s == 0x3f && r == 0x3f) {
|
||
|
vty_out(vty, "debug ospf6 message all\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (s == 0x3f && r == 0) {
|
||
|
vty_out(vty, "debug ospf6 message all send\n");
|
||
|
return 0;
|
||
|
} else if (s == 0 && r == 0x3f) {
|
||
|
vty_out(vty, "debug ospf6 message all recv\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (sh == 0x3f && rh == 0) {
|
||
|
vty_out(vty, "debug ospf6 message all send-hdr\n");
|
||
|
return 0;
|
||
|
} else if (sh == 0 && rh == 0x3f) {
|
||
|
vty_out(vty, "debug ospf6 message all recv-hdr\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Unknown message is logged by default */
|
||
|
if (!IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, SEND)
|
||
|
&& !IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
|
||
|
vty_out(vty, "no debug ospf6 message unknown\n");
|
||
|
else if (!IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, SEND))
|
||
|
vty_out(vty, "no debug ospf6 message unknown send\n");
|
||
|
else if (!IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
|
||
|
vty_out(vty, "no debug ospf6 message unknown recv\n");
|
||
|
|
||
|
for (i = 1; i < 6; i++) {
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, SEND)
|
||
|
&& IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, RECV)) {
|
||
|
vty_out(vty, "debug ospf6 message %s\n", type_str[i]);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, SEND))
|
||
|
vty_out(vty, "debug ospf6 message %s send\n",
|
||
|
type_str[i]);
|
||
|
else if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, SEND_HDR))
|
||
|
vty_out(vty, "debug ospf6 message %s send-hdr\n",
|
||
|
type_str[i]);
|
||
|
|
||
|
if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, RECV))
|
||
|
vty_out(vty, "debug ospf6 message %s recv\n",
|
||
|
type_str[i]);
|
||
|
else if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, RECV_HDR))
|
||
|
vty_out(vty, "debug ospf6 message %s recv-hdr\n",
|
||
|
type_str[i]);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void install_element_ospf6_debug_message(void)
|
||
|
{
|
||
|
install_element(ENABLE_NODE, &debug_ospf6_message_cmd);
|
||
|
install_element(ENABLE_NODE, &no_debug_ospf6_message_cmd);
|
||
|
install_element(CONFIG_NODE, &debug_ospf6_message_cmd);
|
||
|
install_element(CONFIG_NODE, &no_debug_ospf6_message_cmd);
|
||
|
}
|