mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 08:04:27 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			3279 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3279 lines
		
	
	
		
			91 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(p) <= OSPF6_MESSAGE_END(oh);
 | 
						|
		     p += OSPF6_LSA_SIZE(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(p) <= OSPF6_MESSAGE_END(oh);
 | 
						|
	     p += OSPF6_LSA_SIZE(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);
 | 
						|
}
 |