mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-10-31 21:46:33 +00:00 
			
		
		
		
	 d7c0a89a3a
			
		
	
	
		d7c0a89a3a
		
			
		
	
	
	
	
		
			
			The following types are nonstandard: - u_char - u_short - u_int - u_long - u_int8_t - u_int16_t - u_int32_t Replace them with the C99 standard types: - uint8_t - unsigned short - unsigned int - unsigned long - uint8_t - uint16_t - uint32_t Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
		
			
				
	
	
		
			2615 lines
		
	
	
		
			74 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2615 lines
		
	
	
		
			74 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (C) 2003 Yasuhiro Ohara
 | |
|  *
 | |
|  * This file is part of GNU Zebra.
 | |
|  *
 | |
|  * GNU Zebra is free software; you can redistribute it and/or modify it
 | |
|  * under the terms of the GNU General Public License as published by the
 | |
|  * Free Software Foundation; either version 2, or (at your option) any
 | |
|  * later version.
 | |
|  *
 | |
|  * GNU Zebra is distributed in the hope that it will be useful, but
 | |
|  * WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along
 | |
|  * with this program; see the file COPYING; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| #include <zebra.h>
 | |
| 
 | |
| #include "memory.h"
 | |
| #include "log.h"
 | |
| #include "vty.h"
 | |
| #include "command.h"
 | |
| #include "thread.h"
 | |
| #include "linklist.h"
 | |
| 
 | |
| #include "ospf6_proto.h"
 | |
| #include "ospf6_lsa.h"
 | |
| #include "ospf6_lsdb.h"
 | |
| #include "ospf6_network.h"
 | |
| #include "ospf6_message.h"
 | |
| 
 | |
| #include "ospf6_top.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 <netinet/ip6.h>
 | |
| 
 | |
| unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0};
 | |
| static const struct message ospf6_message_type_str[] = {
 | |
| 	{OSPF6_MESSAGE_TYPE_HELLO, "Hello"},
 | |
| 	{OSPF6_MESSAGE_TYPE_DBDESC, "DbDesc"},
 | |
| 	{OSPF6_MESSAGE_TYPE_LSREQ, "LSReq"},
 | |
| 	{OSPF6_MESSAGE_TYPE_LSUPDATE, "LSUpdate"},
 | |
| 	{OSPF6_MESSAGE_TYPE_LSACK, "LSAck"},
 | |
| 	{0}};
 | |
| 
 | |
| /* 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};
 | |
| 
 | |
| /* print functions */
 | |
| 
 | |
| static void ospf6_header_print(struct ospf6_header *oh)
 | |
| {
 | |
| 	char router_id[16], area_id[16];
 | |
| 	inet_ntop(AF_INET, &oh->router_id, router_id, sizeof(router_id));
 | |
| 	inet_ntop(AF_INET, &oh->area_id, area_id, sizeof(area_id));
 | |
| 
 | |
| 	zlog_debug("    OSPFv%d Type:%d Len:%hu Router-ID:%s", oh->version,
 | |
| 		   oh->type, ntohs(oh->length), router_id);
 | |
| 	zlog_debug("    Area-ID:%s Cksum:%hx Instance-ID:%d", area_id,
 | |
| 		   ntohs(oh->checksum), oh->instance_id);
 | |
| }
 | |
| 
 | |
| void ospf6_hello_print(struct ospf6_header *oh)
 | |
| {
 | |
| 	struct ospf6_hello *hello;
 | |
| 	char options[16];
 | |
| 	char drouter[16], bdrouter[16], neighbor[16];
 | |
| 	char *p;
 | |
| 
 | |
| 	ospf6_header_print(oh);
 | |
| 	assert(oh->type == OSPF6_MESSAGE_TYPE_HELLO);
 | |
| 
 | |
| 	hello = (struct ospf6_hello *)((caddr_t)oh
 | |
| 				       + sizeof(struct ospf6_header));
 | |
| 
 | |
| 	inet_ntop(AF_INET, &hello->drouter, drouter, sizeof(drouter));
 | |
| 	inet_ntop(AF_INET, &hello->bdrouter, bdrouter, sizeof(bdrouter));
 | |
| 	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:%s BDR:%s", drouter, bdrouter);
 | |
| 
 | |
| 	for (p = (char *)((caddr_t)hello + sizeof(struct ospf6_hello));
 | |
| 	     p + sizeof(uint32_t) <= OSPF6_MESSAGE_END(oh);
 | |
| 	     p += sizeof(uint32_t)) {
 | |
| 		inet_ntop(AF_INET, (void *)p, neighbor, sizeof(neighbor));
 | |
| 		zlog_debug("    Neighbor: %s", neighbor);
 | |
| 	}
 | |
| 
 | |
| 	assert(p == OSPF6_MESSAGE_END(oh));
 | |
| }
 | |
| 
 | |
| void ospf6_dbdesc_print(struct ospf6_header *oh)
 | |
| {
 | |
| 	struct ospf6_dbdesc *dbdesc;
 | |
| 	char options[16];
 | |
| 	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));
 | |
| 
 | |
| 	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)
 | |
| {
 | |
| 	char id[16], adv_router[16];
 | |
| 	char *p;
 | |
| 
 | |
| 	ospf6_header_print(oh);
 | |
| 	assert(oh->type == OSPF6_MESSAGE_TYPE_LSREQ);
 | |
| 
 | |
| 	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;
 | |
| 		inet_ntop(AF_INET, &e->adv_router, adv_router,
 | |
| 			  sizeof(adv_router));
 | |
| 		inet_ntop(AF_INET, &e->id, id, sizeof(id));
 | |
| 		zlog_debug("    [%s Id:%s Adv:%s]", ospf6_lstype_name(e->type),
 | |
| 			   id, adv_router);
 | |
| 	}
 | |
| 
 | |
| 	assert(p == OSPF6_MESSAGE_END(oh));
 | |
| }
 | |
| 
 | |
| void ospf6_lsupdate_print(struct ospf6_header *oh)
 | |
| {
 | |
| 	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);
 | |
| 
 | |
| 	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)
 | |
| {
 | |
| 	char *p;
 | |
| 
 | |
| 	ospf6_header_print(oh);
 | |
| 	assert(oh->type == OSPF6_MESSAGE_TYPE_LSACK);
 | |
| 
 | |
| 	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 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;
 | |
| 
 | |
| 	hello = (struct ospf6_hello *)((caddr_t)oh
 | |
| 				       + sizeof(struct ospf6_header));
 | |
| 
 | |
| 	/* HelloInterval check */
 | |
| 	if (ntohs(hello->hello_interval) != oi->hello_interval) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 			zlog_debug("HelloInterval mismatch");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* RouterDeadInterval check */
 | |
| 	if (ntohs(hello->dead_interval) != oi->dead_interval) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 			zlog_debug("RouterDeadInterval mismatch");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* E-bit check */
 | |
| 	if (OSPF6_OPT_ISSET(hello->options, OSPF6_OPT_E)
 | |
| 	    != OSPF6_OPT_ISSET(oi->area->options, OSPF6_OPT_E)) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 			zlog_debug("E-bit mismatch");
 | |
| 		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;
 | |
| 	}
 | |
| 
 | |
| 	/* Always override neighbor's source address */
 | |
| 	memcpy(&on->linklocal_addr, src, sizeof(struct in6_addr));
 | |
| 
 | |
| 	/* 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 */
 | |
| 	thread_execute(master, hello_received, on, 0);
 | |
| 	if (twoway)
 | |
| 		thread_execute(master, twoway_received, on, 0);
 | |
| 	else
 | |
| 		thread_execute(master, oneway_received, on, 0);
 | |
| 
 | |
| 	/* Schedule interface events */
 | |
| 	if (backupseen)
 | |
| 		thread_add_event(master, backup_seen, oi, 0, NULL);
 | |
| 	if (neighborchange)
 | |
| 		thread_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))
 | |
| 			zlog_debug("Neighbor state less than Init, ignore");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	switch (on->state) {
 | |
| 	case OSPF6_NEIGHBOR_TWOWAY:
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 			zlog_debug("Neighbor state is 2-Way, ignore");
 | |
| 		return;
 | |
| 
 | |
| 	case OSPF6_NEIGHBOR_INIT:
 | |
| 		thread_execute(master, twoway_received, on, 0);
 | |
| 		if (on->state != OSPF6_NEIGHBOR_EXSTART) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug(
 | |
| 					"Neighbor state is not ExStart, ignore");
 | |
| 			return;
 | |
| 		}
 | |
| 	/* else fall through to ExStart */
 | |
| 	/* fallthru */
 | |
| 	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 */
 | |
| 			thread_execute(master, negotiation_done, on, 0);
 | |
| 
 | |
| 			/* Record neighbor options */
 | |
| 			memcpy(on->options, dbdesc->options,
 | |
| 			       sizeof(on->options));
 | |
| 		} else {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug("Negotiation failed");
 | |
| 			return;
 | |
| 		}
 | |
| 	/* fall through to exchange */
 | |
| 
 | |
| 	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))
 | |
| 				zlog_debug(
 | |
| 					"Duplicated dbdesc discarded by Master, ignore");
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug("Master/Slave bit mismatch");
 | |
| 			thread_add_event(master, seqnumber_mismatch, on, 0,
 | |
| 					 NULL);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug("Initialize bit mismatch");
 | |
| 			thread_add_event(master, seqnumber_mismatch, on, 0,
 | |
| 					 NULL);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (memcmp(on->options, dbdesc->options, sizeof(on->options))) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug("Option field mismatch");
 | |
| 			thread_add_event(master, seqnumber_mismatch, on, 0,
 | |
| 					 NULL);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug(
 | |
| 					"Sequence number mismatch (%#lx expected)",
 | |
| 					(unsigned long)on->dbdesc_seqnum);
 | |
| 			thread_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))
 | |
| 				zlog_debug(
 | |
| 					"Duplicated dbdesc discarded by Master, ignore");
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 			zlog_debug("Not duplicate dbdesc in state %s",
 | |
| 				   ospf6_neighbor_state_str[on->state]);
 | |
| 		thread_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;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if (ntohs(his->header->type) == OSPF6_LSTYPE_AS_EXTERNAL
 | |
| 		    && IS_AREA_STUB(on->ospf6_if->area)) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug(
 | |
| 					"SeqNumMismatch (E-bit mismatch), discard");
 | |
| 			ospf6_lsa_delete(his);
 | |
| 			thread_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)
 | |
| 		thread_add_event(master, ospf6_lsreq_send, on, 0,
 | |
| 				 &on->thread_send_lsreq);
 | |
| 
 | |
| 	THREAD_OFF(on->thread_send_dbdesc);
 | |
| 
 | |
| 	/* More bit check */
 | |
| 	if (!CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MBIT)
 | |
| 	    && !CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT))
 | |
| 		thread_add_event(master, exchange_done, on, 0, NULL);
 | |
| 	else {
 | |
| 		on->thread_send_dbdesc = NULL;
 | |
| 		thread_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))
 | |
| 			zlog_debug("Neighbor state less than Init, ignore");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	switch (on->state) {
 | |
| 	case OSPF6_NEIGHBOR_TWOWAY:
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 			zlog_debug("Neighbor state is 2-Way, ignore");
 | |
| 		return;
 | |
| 
 | |
| 	case OSPF6_NEIGHBOR_INIT:
 | |
| 		thread_execute(master, twoway_received, on, 0);
 | |
| 		if (on->state != OSPF6_NEIGHBOR_EXSTART) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug(
 | |
| 					"Neighbor state is not ExStart, ignore");
 | |
| 			return;
 | |
| 		}
 | |
| 	/* else fall through to ExStart */
 | |
| 	/* fallthru */
 | |
| 	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 */
 | |
| 			thread_execute(master, negotiation_done, on, 0);
 | |
| 
 | |
| 			/* Record neighbor options */
 | |
| 			memcpy(on->options, dbdesc->options,
 | |
| 			       sizeof(on->options));
 | |
| 		} else {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug("Negotiation failed");
 | |
| 			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))
 | |
| 				zlog_debug(
 | |
| 					"Duplicated dbdesc causes retransmit");
 | |
| 			THREAD_OFF(on->thread_send_dbdesc);
 | |
| 			on->thread_send_dbdesc = NULL;
 | |
| 			thread_add_event(master, ospf6_dbdesc_send, on, 0,
 | |
| 					 &on->thread_send_dbdesc);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (!CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug("Master/Slave bit mismatch");
 | |
| 			thread_add_event(master, seqnumber_mismatch, on, 0,
 | |
| 					 NULL);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug("Initialize bit mismatch");
 | |
| 			thread_add_event(master, seqnumber_mismatch, on, 0,
 | |
| 					 NULL);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (memcmp(on->options, dbdesc->options, sizeof(on->options))) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug("Option field mismatch");
 | |
| 			thread_add_event(master, seqnumber_mismatch, on, 0,
 | |
| 					 NULL);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum + 1) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug(
 | |
| 					"Sequence number mismatch (%#lx expected)",
 | |
| 					(unsigned long)on->dbdesc_seqnum + 1);
 | |
| 			thread_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))
 | |
| 				zlog_debug(
 | |
| 					"Duplicated dbdesc causes retransmit");
 | |
| 			THREAD_OFF(on->thread_send_dbdesc);
 | |
| 			thread_add_event(master, ospf6_dbdesc_send, on, 0,
 | |
| 					 &on->thread_send_dbdesc);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 			zlog_debug("Not duplicate dbdesc in state %s",
 | |
| 				   ospf6_neighbor_state_str[on->state]);
 | |
| 		thread_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;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if (OSPF6_LSA_SCOPE(his->header->type) == OSPF6_SCOPE_AS
 | |
| 		    && IS_AREA_STUB(on->ospf6_if->area)) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 				zlog_debug("E-bit mismatch with LSA Headers");
 | |
| 			ospf6_lsa_delete(his);
 | |
| 			thread_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)
 | |
| 		thread_add_event(master, ospf6_lsreq_send, on, 0,
 | |
| 				 &on->thread_send_lsreq);
 | |
| 
 | |
| 	THREAD_OFF(on->thread_send_dbdesc);
 | |
| 	thread_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))
 | |
| 			zlog_debug("Neighbor not found, ignore");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh
 | |
| 					 + sizeof(struct ospf6_header));
 | |
| 
 | |
| 	/* Interface MTU check */
 | |
| 	if (!oi->mtu_ignore && ntohs(dbdesc->ifmtu) != oi->ifmtu) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 			zlog_debug("I/F MTU mismatch");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (dbdesc->reserved1 || dbdesc->reserved2) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 			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(ospf6->router_id))
 | |
| 		ospf6_dbdesc_recv_master(oh, on);
 | |
| 	else if (ntohl(ospf6->router_id) < ntohl(oh->router_id))
 | |
| 		ospf6_dbdesc_recv_slave(oh, on);
 | |
| 	else {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 			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))
 | |
| 			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))
 | |
| 			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;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		/* Find database copy */
 | |
| 		lsa = ospf6_lsdb_lookup(e->type, e->id, e->adv_router, lsdb);
 | |
| 		if (lsa == NULL) {
 | |
| 			char id[16], adv_router[16];
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) {
 | |
| 				inet_ntop(AF_INET, &e->id, id, sizeof(id));
 | |
| 				inet_ntop(AF_INET, &e->adv_router, adv_router,
 | |
| 					  sizeof(adv_router));
 | |
| 				zlog_debug(
 | |
| 					"Can't find requested [%s Id:%s Adv:%s]",
 | |
| 					ospf6_lstype_name(e->type), id,
 | |
| 					adv_router);
 | |
| 			}
 | |
| 			thread_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 */
 | |
| 	THREAD_OFF(on->thread_send_lsupdate);
 | |
| 	thread_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) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
 | |
| 						   RECV))
 | |
| 				zlog_debug("%s: undersized IPv6 prefix header",
 | |
| 					   __func__);
 | |
| 			return MSG_NG;
 | |
| 		}
 | |
| 		/* safe to look deeper */
 | |
| 		if (current->prefix_length > IPV6_MAX_BITLEN) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
 | |
| 						   RECV))
 | |
| 				zlog_debug("%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) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
 | |
| 						   RECV))
 | |
| 				zlog_debug("%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) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug(
 | |
| 				"%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) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug("%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) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
 | |
| 						   RECV))
 | |
| 				zlog_debug(
 | |
| 					"%s: 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) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
 | |
| 						   RECV))
 | |
| 				zlog_debug(
 | |
| 					"%s: 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) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
 | |
| 						   RECV))
 | |
| 				zlog_debug("%s: 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) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
 | |
| 						   RECV))
 | |
| 				zlog_debug("%s: 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) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
 | |
| 						   RECV))
 | |
| 				zlog_debug("%s: 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 */
 | |
| 			);
 | |
| 	}
 | |
| 	/* 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) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
 | |
| 						   RECV))
 | |
| 				zlog_debug(
 | |
| 					"%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) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
 | |
| 						   RECV))
 | |
| 				zlog_debug(
 | |
| 					"%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)) {
 | |
| 				if (IS_OSPF6_DEBUG_MESSAGE(
 | |
| 					    OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 					zlog_debug(
 | |
| 						"%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) {
 | |
| 				if (IS_OSPF6_DEBUG_MESSAGE(
 | |
| 					    OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 					zlog_debug(
 | |
| 						"%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)) {
 | |
| 				if (IS_OSPF6_DEBUG_MESSAGE(
 | |
| 					    OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 					zlog_debug(
 | |
| 						"%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) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug(
 | |
| 				"%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) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug("%s: undersized (%u B) packet", __func__,
 | |
| 				   bytesonwire);
 | |
| 		return MSG_NG;
 | |
| 	}
 | |
| 	/* Now it is safe to access header fields. */
 | |
| 	if (bytesonwire != ntohs(oh->length)) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug(
 | |
| 				"%s: %s packet length error (%u real, %u declared)",
 | |
| 				__func__, lookup_msg(ospf6_message_type_str,
 | |
| 						     oh->type, NULL),
 | |
| 				bytesonwire, ntohs(oh->length));
 | |
| 		return MSG_NG;
 | |
| 	}
 | |
| 	/* version check */
 | |
| 	if (oh->version != OSPFV3_VERSION) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug("%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]) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug("%s: undersized (%u B) %s packet", __func__,
 | |
| 				   bytesonwire,
 | |
| 				   lookup_msg(ospf6_message_type_str, oh->type,
 | |
| 					      NULL));
 | |
| 		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;
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug("%s: alignment error in %s packet", __func__,
 | |
| 				   lookup_msg(ospf6_message_type_str, oh->type,
 | |
| 					      NULL));
 | |
| 		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;
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug("%s: alignment error in %s packet", __func__,
 | |
| 				   lookup_msg(ospf6_message_type_str, oh->type,
 | |
| 					      NULL));
 | |
| 		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:
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug("%s: invalid (%u) message type", __func__,
 | |
| 				   oh->type);
 | |
| 		return MSG_NG;
 | |
| 	}
 | |
| 	if (test != MSG_OK
 | |
| 	    && IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 		zlog_debug("%s: anomaly in %s packet", __func__,
 | |
| 			   lookup_msg(ospf6_message_type_str, oh->type, NULL));
 | |
| 	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)
 | |
| {
 | |
| 	char buf[2][INET_ADDRSTRLEN];
 | |
| 
 | |
| 	if (MSG_OK != ospf6_packet_examin(oh, bytesonwire))
 | |
| 		return MSG_NG;
 | |
| 
 | |
| 	/* Area-ID check */
 | |
| 	if (oh->area_id != oi->area->area_id) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) {
 | |
| 			if (oh->area_id == OSPF_AREA_BACKBONE)
 | |
| 				zlog_debug(
 | |
| 					"%s: Message may be via Virtual Link: not supported",
 | |
| 					__func__);
 | |
| 			else
 | |
| 				zlog_debug(
 | |
| 					"%s: Area-ID mismatch (my %s, rcvd %s)",
 | |
| 					__func__,
 | |
| 					inet_ntop(AF_INET, &oi->area->area_id,
 | |
| 						  buf[0], INET_ADDRSTRLEN),
 | |
| 					inet_ntop(AF_INET, &oh->area_id, buf[1],
 | |
| 						  INET_ADDRSTRLEN));
 | |
| 		}
 | |
| 		return MSG_NG;
 | |
| 	}
 | |
| 
 | |
| 	/* Instance-ID check */
 | |
| 	if (oh->instance_id != oi->instance_id) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
 | |
| 			zlog_debug("%s: Instance-ID mismatch (my %u, rcvd %u)",
 | |
| 				   __func__, oi->instance_id, oh->instance_id);
 | |
| 		return MSG_NG;
 | |
| 	}
 | |
| 
 | |
| 	/* Router-ID check */
 | |
| 	if (oh->router_id == oi->area->ospf6->router_id) {
 | |
| 		zlog_warn("%s: Duplicate Router-ID (%s)", __func__,
 | |
| 			  inet_ntop(AF_INET, &oh->router_id, buf[0],
 | |
| 				    INET_ADDRSTRLEN));
 | |
| 		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))
 | |
| 			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))
 | |
| 			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))
 | |
| 			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))
 | |
| 			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;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		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)
 | |
| {
 | |
| 	uint8_t *recvnew, *sendnew;
 | |
| 
 | |
| 	if (size <= iobuflen)
 | |
| 		return iobuflen;
 | |
| 
 | |
| 	recvnew = XMALLOC(MTYPE_OSPF6_MESSAGE, size);
 | |
| 	sendnew = XMALLOC(MTYPE_OSPF6_MESSAGE, size);
 | |
| 	if (recvnew == NULL || sendnew == NULL) {
 | |
| 		if (recvnew)
 | |
| 			XFREE(MTYPE_OSPF6_MESSAGE, recvnew);
 | |
| 		if (sendnew)
 | |
| 			XFREE(MTYPE_OSPF6_MESSAGE, sendnew);
 | |
| 		zlog_debug("Could not allocate I/O buffer of size %d.", size);
 | |
| 		return iobuflen;
 | |
| 	}
 | |
| 
 | |
| 	if (recvbuf)
 | |
| 		XFREE(MTYPE_OSPF6_MESSAGE, recvbuf);
 | |
| 	if (sendbuf)
 | |
| 		XFREE(MTYPE_OSPF6_MESSAGE, sendbuf);
 | |
| 	recvbuf = recvnew;
 | |
| 	sendbuf = sendnew;
 | |
| 	iobuflen = size;
 | |
| 
 | |
| 	return iobuflen;
 | |
| }
 | |
| 
 | |
| void ospf6_message_terminate(void)
 | |
| {
 | |
| 	if (recvbuf) {
 | |
| 		XFREE(MTYPE_OSPF6_MESSAGE, recvbuf);
 | |
| 		recvbuf = NULL;
 | |
| 	}
 | |
| 
 | |
| 	if (sendbuf) {
 | |
| 		XFREE(MTYPE_OSPF6_MESSAGE, sendbuf);
 | |
| 		sendbuf = NULL;
 | |
| 	}
 | |
| 
 | |
| 	iobuflen = 0;
 | |
| }
 | |
| 
 | |
| int ospf6_receive(struct thread *thread)
 | |
| {
 | |
| 	int sockfd;
 | |
| 	unsigned int len;
 | |
| 	char srcname[64], dstname[64];
 | |
| 	struct in6_addr src, dst;
 | |
| 	ifindex_t ifindex;
 | |
| 	struct iovec iovector[2];
 | |
| 	struct ospf6_interface *oi;
 | |
| 	struct ospf6_header *oh;
 | |
| 
 | |
| 	/* add next read thread */
 | |
| 	sockfd = THREAD_FD(thread);
 | |
| 	thread_add_read(master, ospf6_receive, NULL, sockfd, NULL);
 | |
| 
 | |
| 	/* initialize */
 | |
| 	memset(&src, 0, sizeof(src));
 | |
| 	memset(&dst, 0, sizeof(dst));
 | |
| 	ifindex = 0;
 | |
| 	memset(recvbuf, 0, iobuflen);
 | |
| 	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);
 | |
| 	if (len > iobuflen) {
 | |
| 		zlog_err("Excess message read");
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	oi = ospf6_interface_lookup_by_ifindex(ifindex);
 | |
| 	if (oi == NULL || oi->area == NULL
 | |
| 	    || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug("Message received on disabled interface");
 | |
| 		return 0;
 | |
| 	}
 | |
| 	if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV))
 | |
| 			zlog_debug("%s: Ignore message on passive interface %s",
 | |
| 				   __func__, oi->interface->name);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	oh = (struct ospf6_header *)recvbuf;
 | |
| 	if (ospf6_rxpacket_examin(oi, oh, len) != MSG_OK)
 | |
| 		return 0;
 | |
| 
 | |
| 	/* 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)) {
 | |
| 		inet_ntop(AF_INET6, &src, srcname, sizeof(srcname));
 | |
| 		inet_ntop(AF_INET6, &dst, dstname, sizeof(dstname));
 | |
| 		zlog_debug("%s received on %s",
 | |
| 			   lookup_msg(ospf6_message_type_str, oh->type, NULL),
 | |
| 			   oi->interface->name);
 | |
| 		zlog_debug("    src: %s", srcname);
 | |
| 		zlog_debug("    dst: %s", dstname);
 | |
| 
 | |
| 		switch (oh->type) {
 | |
| 		case OSPF6_MESSAGE_TYPE_HELLO:
 | |
| 			ospf6_hello_print(oh);
 | |
| 			break;
 | |
| 		case OSPF6_MESSAGE_TYPE_DBDESC:
 | |
| 			ospf6_dbdesc_print(oh);
 | |
| 			break;
 | |
| 		case OSPF6_MESSAGE_TYPE_LSREQ:
 | |
| 			ospf6_lsreq_print(oh);
 | |
| 			break;
 | |
| 		case OSPF6_MESSAGE_TYPE_LSUPDATE:
 | |
| 			ospf6_lsupdate_print(oh);
 | |
| 			break;
 | |
| 		case OSPF6_MESSAGE_TYPE_LSACK:
 | |
| 			ospf6_lsack_print(oh);
 | |
| 			break;
 | |
| 		default:
 | |
| 			assert(0);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	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 0;
 | |
| }
 | |
| 
 | |
| static void ospf6_send(struct in6_addr *src, struct in6_addr *dst,
 | |
| 		       struct ospf6_interface *oi, struct ospf6_header *oh)
 | |
| {
 | |
| 	unsigned int len;
 | |
| 	char srcname[64], dstname[64];
 | |
| 	struct iovec iovector[2];
 | |
| 
 | |
| 	/* initialize */
 | |
| 	iovector[0].iov_base = (caddr_t)oh;
 | |
| 	iovector[0].iov_len = ntohs(oh->length);
 | |
| 	iovector[1].iov_base = NULL;
 | |
| 	iovector[1].iov_len = 0;
 | |
| 
 | |
| 	/* fill OSPF header */
 | |
| 	oh->version = OSPFV3_VERSION;
 | |
| 	/* message type must be set before */
 | |
| 	/* message length must be set before */
 | |
| 	oh->router_id = oi->area->ospf6->router_id;
 | |
| 	oh->area_id = oi->area->area_id;
 | |
| 	/* checksum is calculated by kernel */
 | |
| 	oh->instance_id = oi->instance_id;
 | |
| 	oh->reserved = 0;
 | |
| 
 | |
| 	/* Log */
 | |
| 	if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)) {
 | |
| 		inet_ntop(AF_INET6, dst, dstname, sizeof(dstname));
 | |
| 		if (src)
 | |
| 			inet_ntop(AF_INET6, src, srcname, sizeof(srcname));
 | |
| 		else
 | |
| 			memset(srcname, 0, sizeof(srcname));
 | |
| 		zlog_debug("%s send on %s",
 | |
| 			   lookup_msg(ospf6_message_type_str, oh->type, NULL),
 | |
| 			   oi->interface->name);
 | |
| 		zlog_debug("    src: %s", srcname);
 | |
| 		zlog_debug("    dst: %s", dstname);
 | |
| 
 | |
| 		switch (oh->type) {
 | |
| 		case OSPF6_MESSAGE_TYPE_HELLO:
 | |
| 			ospf6_hello_print(oh);
 | |
| 			break;
 | |
| 		case OSPF6_MESSAGE_TYPE_DBDESC:
 | |
| 			ospf6_dbdesc_print(oh);
 | |
| 			break;
 | |
| 		case OSPF6_MESSAGE_TYPE_LSREQ:
 | |
| 			ospf6_lsreq_print(oh);
 | |
| 			break;
 | |
| 		case OSPF6_MESSAGE_TYPE_LSUPDATE:
 | |
| 			ospf6_lsupdate_print(oh);
 | |
| 			break;
 | |
| 		case OSPF6_MESSAGE_TYPE_LSACK:
 | |
| 			ospf6_lsack_print(oh);
 | |
| 			break;
 | |
| 		default:
 | |
| 			zlog_debug("Unknown message");
 | |
| 			assert(0);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* send message */
 | |
| 	len = ospf6_sendmsg(src, dst, &oi->interface->ifindex, iovector);
 | |
| 	if (len != ntohs(oh->length))
 | |
| 		zlog_err("Could not send entire message");
 | |
| }
 | |
| 
 | |
| static uint32_t ospf6_packet_max(struct ospf6_interface *oi)
 | |
| {
 | |
| 	assert(oi->ifmtu > sizeof(struct ip6_hdr));
 | |
| 	return oi->ifmtu - (sizeof(struct ip6_hdr));
 | |
| }
 | |
| 
 | |
| int ospf6_hello_send(struct thread *thread)
 | |
| {
 | |
| 	struct ospf6_interface *oi;
 | |
| 	struct ospf6_header *oh;
 | |
| 	struct ospf6_hello *hello;
 | |
| 	uint8_t *p;
 | |
| 	struct listnode *node, *nnode;
 | |
| 	struct ospf6_neighbor *on;
 | |
| 
 | |
| 	oi = (struct ospf6_interface *)THREAD_ARG(thread);
 | |
| 	oi->thread_send_hello = (struct thread *)NULL;
 | |
| 
 | |
| 	if (oi->state <= OSPF6_INTERFACE_DOWN) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND))
 | |
| 			zlog_debug("Unable to send Hello on down interface %s",
 | |
| 				   oi->interface->name);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (iobuflen == 0) {
 | |
| 		zlog_debug("Unable to send Hello on interface %s iobuflen is 0",
 | |
| 			   oi->interface->name);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	/* set next thread */
 | |
| 	thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval,
 | |
| 			 &oi->thread_send_hello);
 | |
| 
 | |
| 	memset(sendbuf, 0, iobuflen);
 | |
| 	oh = (struct ospf6_header *)sendbuf;
 | |
| 	hello = (struct ospf6_hello *)((caddr_t)oh
 | |
| 				       + sizeof(struct ospf6_header));
 | |
| 
 | |
| 	hello->interface_id = htonl(oi->interface->ifindex);
 | |
| 	hello->priority = oi->priority;
 | |
| 	hello->options[0] = oi->area->options[0];
 | |
| 	hello->options[1] = oi->area->options[1];
 | |
| 	hello->options[2] = oi->area->options[2];
 | |
| 	hello->hello_interval = htons(oi->hello_interval);
 | |
| 	hello->dead_interval = htons(oi->dead_interval);
 | |
| 	hello->drouter = oi->drouter;
 | |
| 	hello->bdrouter = oi->bdrouter;
 | |
| 
 | |
| 	p = (uint8_t *)((caddr_t)hello + sizeof(struct ospf6_hello));
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) {
 | |
| 		if (on->state < OSPF6_NEIGHBOR_INIT)
 | |
| 			continue;
 | |
| 
 | |
| 		if (p - sendbuf + sizeof(uint32_t) > ospf6_packet_max(oi)) {
 | |
| 			if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO,
 | |
| 						   SEND))
 | |
| 				zlog_debug(
 | |
| 					"sending Hello message: exceeds I/F MTU");
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		memcpy(p, &on->router_id, sizeof(uint32_t));
 | |
| 		p += sizeof(uint32_t);
 | |
| 	}
 | |
| 
 | |
| 	oh->type = OSPF6_MESSAGE_TYPE_HELLO;
 | |
| 	oh->length = htons(p - sendbuf);
 | |
| 
 | |
| 	oi->hello_out++;
 | |
| 
 | |
| 	ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int ospf6_dbdesc_send(struct thread *thread)
 | |
| {
 | |
| 	struct ospf6_neighbor *on;
 | |
| 	struct ospf6_header *oh;
 | |
| 	struct ospf6_dbdesc *dbdesc;
 | |
| 	uint8_t *p;
 | |
| 	struct ospf6_lsa *lsa;
 | |
| 	struct in6_addr *dst;
 | |
| 
 | |
| 	on = (struct ospf6_neighbor *)THREAD_ARG(thread);
 | |
| 	on->thread_send_dbdesc = (struct thread *)NULL;
 | |
| 
 | |
| 	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 0;
 | |
| 	}
 | |
| 
 | |
| 	/* set next thread if master */
 | |
| 	if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT))
 | |
| 		thread_add_timer(master, ospf6_dbdesc_send, on,
 | |
| 				 on->ospf6_if->rxmt_interval,
 | |
| 				 &on->thread_send_dbdesc);
 | |
| 
 | |
| 	memset(sendbuf, 0, iobuflen);
 | |
| 	oh = (struct ospf6_header *)sendbuf;
 | |
| 	dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh
 | |
| 					 + sizeof(struct ospf6_header));
 | |
| 
 | |
| 	/* 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 = monotime(NULL);
 | |
| 	}
 | |
| 
 | |
| 	dbdesc->options[0] = on->ospf6_if->area->options[0];
 | |
| 	dbdesc->options[1] = on->ospf6_if->area->options[1];
 | |
| 	dbdesc->options[2] = on->ospf6_if->area->options[2];
 | |
| 	dbdesc->ifmtu = htons(on->ospf6_if->ifmtu);
 | |
| 	dbdesc->bits = on->dbdesc_bits;
 | |
| 	dbdesc->seqnum = htonl(on->dbdesc_seqnum);
 | |
| 
 | |
| 	/* if this is not initial one, set LSA headers in dbdesc */
 | |
| 	p = (uint8_t *)((caddr_t)dbdesc + sizeof(struct ospf6_dbdesc));
 | |
| 	if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)) {
 | |
| 		for (ALL_LSDB(on->dbdesc_list, lsa)) {
 | |
| 			ospf6_lsa_age_update_to_send(lsa,
 | |
| 						     on->ospf6_if->transdelay);
 | |
| 
 | |
| 			/* MTU check */
 | |
| 			if (p - sendbuf + sizeof(struct ospf6_lsa_header)
 | |
| 			    > ospf6_packet_max(on->ospf6_if)) {
 | |
| 				ospf6_lsdb_lsa_unlock(lsa);
 | |
| 				break;
 | |
| 			}
 | |
| 			memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header));
 | |
| 			p += sizeof(struct ospf6_lsa_header);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	oh->type = OSPF6_MESSAGE_TYPE_DBDESC;
 | |
| 	oh->length = htons(p - sendbuf);
 | |
| 
 | |
| 
 | |
| 	if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
 | |
| 		dst = &allspfrouters6;
 | |
| 	else
 | |
| 		dst = &on->linklocal_addr;
 | |
| 
 | |
| 	on->ospf6_if->db_desc_out++;
 | |
| 
 | |
| 	ospf6_send(on->ospf6_if->linklocal_addr, dst, on->ospf6_if, oh);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int ospf6_dbdesc_send_newone(struct thread *thread)
 | |
| {
 | |
| 	struct ospf6_neighbor *on;
 | |
| 	struct ospf6_lsa *lsa;
 | |
| 	unsigned int size = 0;
 | |
| 
 | |
| 	on = (struct ospf6_neighbor *)THREAD_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)) {
 | |
| 		if (size + sizeof(struct ospf6_lsa_header)
 | |
| 		    > ospf6_packet_max(on->ospf6_if)) {
 | |
| 			ospf6_lsdb_lsa_unlock(lsa);
 | |
| 			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))
 | |
| 		thread_add_event(master, exchange_done, on, 0, NULL);
 | |
| 
 | |
| 	thread_execute(master, ospf6_dbdesc_send, on, 0);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int ospf6_lsreq_send(struct thread *thread)
 | |
| {
 | |
| 	struct ospf6_neighbor *on;
 | |
| 	struct ospf6_header *oh;
 | |
| 	struct ospf6_lsreq_entry *e;
 | |
| 	uint8_t *p;
 | |
| 	struct ospf6_lsa *lsa, *last_req;
 | |
| 
 | |
| 	on = (struct ospf6_neighbor *)THREAD_ARG(thread);
 | |
| 	on->thread_send_lsreq = (struct thread *)NULL;
 | |
| 
 | |
| 	/* 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))
 | |
| 			zlog_debug("Quit to send LSReq to neighbor %s state %s",
 | |
| 				   on->name,
 | |
| 				   ospf6_neighbor_state_str[on->state]);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	/* schedule loading_done if request list is empty */
 | |
| 	if (on->request_list->count == 0) {
 | |
| 		thread_add_event(master, loading_done, on, 0, NULL);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	memset(sendbuf, 0, iobuflen);
 | |
| 	oh = (struct ospf6_header *)sendbuf;
 | |
| 	last_req = NULL;
 | |
| 
 | |
| 	/* set Request entries in lsreq */
 | |
| 	p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header));
 | |
| 	for (ALL_LSDB(on->request_list, lsa)) {
 | |
| 		/* MTU check */
 | |
| 		if (p - sendbuf + sizeof(struct ospf6_lsreq_entry)
 | |
| 		    > ospf6_packet_max(on->ospf6_if)) {
 | |
| 			ospf6_lsdb_lsa_unlock(lsa);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		e = (struct ospf6_lsreq_entry *)p;
 | |
| 		e->type = lsa->header->type;
 | |
| 		e->id = lsa->header->id;
 | |
| 		e->adv_router = lsa->header->adv_router;
 | |
| 		p += 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;
 | |
| 	}
 | |
| 
 | |
| 	oh->type = OSPF6_MESSAGE_TYPE_LSREQ;
 | |
| 	oh->length = htons(p - sendbuf);
 | |
| 
 | |
| 	on->ospf6_if->ls_req_out++;
 | |
| 
 | |
| 	if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
 | |
| 		ospf6_send(on->ospf6_if->linklocal_addr, &allspfrouters6,
 | |
| 			   on->ospf6_if, oh);
 | |
| 	else
 | |
| 		ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr,
 | |
| 			   on->ospf6_if, oh);
 | |
| 
 | |
| 	/* set next thread */
 | |
| 	if (on->request_list->count != 0) {
 | |
| 		on->thread_send_lsreq = NULL;
 | |
| 		thread_add_timer(master, ospf6_lsreq_send, on,
 | |
| 				 on->ospf6_if->rxmt_interval,
 | |
| 				 &on->thread_send_lsreq);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void ospf6_send_lsupdate(struct ospf6_neighbor *on,
 | |
| 				struct ospf6_interface *oi,
 | |
| 				struct ospf6_header *oh)
 | |
| {
 | |
| 
 | |
| 	if (on) {
 | |
| 		on->ospf6_if->ls_upd_out++;
 | |
| 
 | |
| 		if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
 | |
| 		    || (on->ospf6_if->state == OSPF6_INTERFACE_DR)
 | |
| 		    || (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) {
 | |
| 			ospf6_send(on->ospf6_if->linklocal_addr,
 | |
| 				   &allspfrouters6, on->ospf6_if, oh);
 | |
| 		} else {
 | |
| 			ospf6_send(on->ospf6_if->linklocal_addr,
 | |
| 				   &on->linklocal_addr, on->ospf6_if, oh);
 | |
| 		}
 | |
| 	} else if (oi) {
 | |
| 
 | |
| 		oi->ls_upd_out++;
 | |
| 
 | |
| 		if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT)
 | |
| 		    || (oi->state == OSPF6_INTERFACE_DR)
 | |
| 		    || (oi->state == OSPF6_INTERFACE_BDR)) {
 | |
| 			ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh);
 | |
| 		} else {
 | |
| 			ospf6_send(oi->linklocal_addr, &alldrouters6, oi, oh);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int ospf6_lsupdate_send_neighbor(struct thread *thread)
 | |
| {
 | |
| 	struct ospf6_neighbor *on;
 | |
| 	struct ospf6_header *oh;
 | |
| 	struct ospf6_lsupdate *lsupdate;
 | |
| 	uint8_t *p;
 | |
| 	int lsa_cnt;
 | |
| 	struct ospf6_lsa *lsa;
 | |
| 
 | |
| 	on = (struct ospf6_neighbor *)THREAD_ARG(thread);
 | |
| 	on->thread_send_lsupdate = (struct thread *)NULL;
 | |
| 
 | |
| 	if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
 | |
| 		zlog_debug("LSUpdate to neighbor %s", on->name);
 | |
| 
 | |
| 	if (on->state < OSPF6_NEIGHBOR_EXCHANGE) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
 | |
| 			zlog_debug("Quit to send (neighbor state %s)",
 | |
| 				   ospf6_neighbor_state_str[on->state]);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	memset(sendbuf, 0, iobuflen);
 | |
| 	oh = (struct ospf6_header *)sendbuf;
 | |
| 	lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
 | |
| 					     + sizeof(struct ospf6_header));
 | |
| 
 | |
| 	p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
 | |
| 	lsa_cnt = 0;
 | |
| 
 | |
| 	/* lsupdate_list lists those LSA which doesn't need to be
 | |
| 	   retransmitted. remove those from the list */
 | |
| 	for (ALL_LSDB(on->lsupdate_list, lsa)) {
 | |
| 		/* MTU check */
 | |
| 		if ((p - sendbuf + (unsigned int)OSPF6_LSA_SIZE(lsa->header))
 | |
| 		    > ospf6_packet_max(on->ospf6_if)) {
 | |
| 			if (lsa_cnt) {
 | |
| 				oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
 | |
| 				oh->length = htons(p - sendbuf);
 | |
| 				lsupdate->lsa_number = htonl(lsa_cnt);
 | |
| 
 | |
| 				ospf6_send_lsupdate(on, NULL, oh);
 | |
| 
 | |
| 				memset(sendbuf, 0, iobuflen);
 | |
| 				oh = (struct ospf6_header *)sendbuf;
 | |
| 				lsupdate = (struct ospf6_lsupdate
 | |
| 						    *)((caddr_t)oh
 | |
| 						       + sizeof(struct
 | |
| 								ospf6_header));
 | |
| 
 | |
| 				p = (uint8_t *)((caddr_t)lsupdate
 | |
| 						+ sizeof(struct
 | |
| 							 ospf6_lsupdate));
 | |
| 				lsa_cnt = 0;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
 | |
| 		memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
 | |
| 		p += OSPF6_LSA_SIZE(lsa->header);
 | |
| 		lsa_cnt++;
 | |
| 
 | |
| 		assert(lsa->lock == 2);
 | |
| 		ospf6_lsdb_remove(lsa, on->lsupdate_list);
 | |
| 	}
 | |
| 
 | |
| 	if (lsa_cnt) {
 | |
| 		oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
 | |
| 		oh->length = htons(p - sendbuf);
 | |
| 		lsupdate->lsa_number = htonl(lsa_cnt);
 | |
| 		ospf6_send_lsupdate(on, NULL, oh);
 | |
| 	}
 | |
| 
 | |
| 	/* The addresses used for retransmissions are different from those sent
 | |
| 	   the
 | |
| 	   first time and so we need to separate them here.
 | |
| 	*/
 | |
| 	memset(sendbuf, 0, iobuflen);
 | |
| 	oh = (struct ospf6_header *)sendbuf;
 | |
| 	lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
 | |
| 					     + sizeof(struct ospf6_header));
 | |
| 	p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
 | |
| 	lsa_cnt = 0;
 | |
| 
 | |
| 	for (ALL_LSDB(on->retrans_list, lsa)) {
 | |
| 		/* MTU check */
 | |
| 		if ((p - sendbuf + (unsigned int)OSPF6_LSA_SIZE(lsa->header))
 | |
| 		    > ospf6_packet_max(on->ospf6_if)) {
 | |
| 			if (lsa_cnt) {
 | |
| 				oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
 | |
| 				oh->length = htons(p - sendbuf);
 | |
| 				lsupdate->lsa_number = htonl(lsa_cnt);
 | |
| 
 | |
| 				if (on->ospf6_if->state
 | |
| 				    == OSPF6_INTERFACE_POINTTOPOINT) {
 | |
| 					ospf6_send(on->ospf6_if->linklocal_addr,
 | |
| 						   &allspfrouters6,
 | |
| 						   on->ospf6_if, oh);
 | |
| 				} else {
 | |
| 					ospf6_send(on->ospf6_if->linklocal_addr,
 | |
| 						   &on->linklocal_addr,
 | |
| 						   on->ospf6_if, oh);
 | |
| 				}
 | |
| 
 | |
| 				memset(sendbuf, 0, iobuflen);
 | |
| 				oh = (struct ospf6_header *)sendbuf;
 | |
| 				lsupdate = (struct ospf6_lsupdate
 | |
| 						    *)((caddr_t)oh
 | |
| 						       + sizeof(struct
 | |
| 								ospf6_header));
 | |
| 				p = (uint8_t *)((caddr_t)lsupdate
 | |
| 						+ sizeof(struct
 | |
| 							 ospf6_lsupdate));
 | |
| 				lsa_cnt = 0;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
 | |
| 		memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
 | |
| 		p += OSPF6_LSA_SIZE(lsa->header);
 | |
| 		lsa_cnt++;
 | |
| 	}
 | |
| 
 | |
| 	if (lsa_cnt) {
 | |
| 		oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
 | |
| 		oh->length = htons(p - sendbuf);
 | |
| 		lsupdate->lsa_number = htonl(lsa_cnt);
 | |
| 
 | |
| 		if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
 | |
| 			ospf6_send(on->ospf6_if->linklocal_addr,
 | |
| 				   &allspfrouters6, on->ospf6_if, oh);
 | |
| 		else
 | |
| 			ospf6_send(on->ospf6_if->linklocal_addr,
 | |
| 				   &on->linklocal_addr, on->ospf6_if, oh);
 | |
| 	}
 | |
| 
 | |
| 	if (on->lsupdate_list->count != 0) {
 | |
| 		on->thread_send_lsupdate = NULL;
 | |
| 		thread_add_event(master, ospf6_lsupdate_send_neighbor, on, 0,
 | |
| 				 &on->thread_send_lsupdate);
 | |
| 	} else if (on->retrans_list->count != 0) {
 | |
| 		on->thread_send_lsupdate = NULL;
 | |
| 		thread_add_timer(master, ospf6_lsupdate_send_neighbor, on,
 | |
| 				 on->ospf6_if->rxmt_interval,
 | |
| 				 &on->thread_send_lsupdate);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on,
 | |
| 				     struct ospf6_lsa *lsa)
 | |
| {
 | |
| 	struct ospf6_header *oh;
 | |
| 	struct ospf6_lsupdate *lsupdate;
 | |
| 	uint8_t *p;
 | |
| 	int lsa_cnt = 0;
 | |
| 
 | |
| 	memset(sendbuf, 0, iobuflen);
 | |
| 	oh = (struct ospf6_header *)sendbuf;
 | |
| 	lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
 | |
| 					     + sizeof(struct ospf6_header));
 | |
| 
 | |
| 	p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
 | |
| 	ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
 | |
| 	memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
 | |
| 	p += OSPF6_LSA_SIZE(lsa->header);
 | |
| 	lsa_cnt++;
 | |
| 
 | |
| 	oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
 | |
| 	oh->length = htons(p - sendbuf);
 | |
| 	lsupdate->lsa_number = htonl(lsa_cnt);
 | |
| 
 | |
| 	if (IS_OSPF6_DEBUG_FLOODING
 | |
| 	    || IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
 | |
| 		zlog_debug("%s: Send lsupdate with lsa %s (age %u)",
 | |
| 			   __PRETTY_FUNCTION__, lsa->name,
 | |
| 			   ntohs(lsa->header->age));
 | |
| 
 | |
| 	ospf6_send_lsupdate(on, NULL, oh);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int ospf6_lsupdate_send_interface(struct thread *thread)
 | |
| {
 | |
| 	struct ospf6_interface *oi;
 | |
| 	struct ospf6_header *oh;
 | |
| 	struct ospf6_lsupdate *lsupdate;
 | |
| 	uint8_t *p;
 | |
| 	int lsa_cnt;
 | |
| 	struct ospf6_lsa *lsa;
 | |
| 
 | |
| 	oi = (struct ospf6_interface *)THREAD_ARG(thread);
 | |
| 	oi->thread_send_lsupdate = (struct thread *)NULL;
 | |
| 
 | |
| 	if (oi->state <= OSPF6_INTERFACE_WAITING) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
 | |
| 			zlog_debug(
 | |
| 				"Quit to send LSUpdate to interface %s state %s",
 | |
| 				oi->interface->name,
 | |
| 				ospf6_interface_state_str[oi->state]);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	/* if we have nothing to send, return */
 | |
| 	if (oi->lsupdate_list->count == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	memset(sendbuf, 0, iobuflen);
 | |
| 	oh = (struct ospf6_header *)sendbuf;
 | |
| 	lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
 | |
| 					     + sizeof(struct ospf6_header));
 | |
| 
 | |
| 	p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
 | |
| 	lsa_cnt = 0;
 | |
| 
 | |
| 	for (ALL_LSDB(oi->lsupdate_list, lsa)) {
 | |
| 		/* MTU check */
 | |
| 		if ((p - sendbuf + ((unsigned int)OSPF6_LSA_SIZE(lsa->header)))
 | |
| 		    > ospf6_packet_max(oi)) {
 | |
| 			if (lsa_cnt) {
 | |
| 				oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
 | |
| 				oh->length = htons(p - sendbuf);
 | |
| 				lsupdate->lsa_number = htonl(lsa_cnt);
 | |
| 
 | |
| 				ospf6_send_lsupdate(NULL, oi, oh);
 | |
| 				if (IS_OSPF6_DEBUG_MESSAGE(
 | |
| 					    OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
 | |
| 					zlog_debug("%s: LSUpdate length %d",
 | |
| 						   __PRETTY_FUNCTION__,
 | |
| 						   ntohs(oh->length));
 | |
| 
 | |
| 				memset(sendbuf, 0, iobuflen);
 | |
| 				oh = (struct ospf6_header *)sendbuf;
 | |
| 				lsupdate = (struct ospf6_lsupdate
 | |
| 						    *)((caddr_t)oh
 | |
| 						       + sizeof(struct
 | |
| 								ospf6_header));
 | |
| 
 | |
| 				p = (uint8_t *)((caddr_t)lsupdate
 | |
| 						+ sizeof(struct
 | |
| 							 ospf6_lsupdate));
 | |
| 				lsa_cnt = 0;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
 | |
| 		memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
 | |
| 		p += OSPF6_LSA_SIZE(lsa->header);
 | |
| 		lsa_cnt++;
 | |
| 
 | |
| 		assert(lsa->lock == 2);
 | |
| 		ospf6_lsdb_remove(lsa, oi->lsupdate_list);
 | |
| 	}
 | |
| 
 | |
| 	if (lsa_cnt) {
 | |
| 		oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
 | |
| 		oh->length = htons(p - sendbuf);
 | |
| 		lsupdate->lsa_number = htonl(lsa_cnt);
 | |
| 
 | |
| 		ospf6_send_lsupdate(NULL, oi, oh);
 | |
| 	}
 | |
| 
 | |
| 	if (oi->lsupdate_list->count > 0) {
 | |
| 		oi->thread_send_lsupdate = NULL;
 | |
| 		thread_add_event(master, ospf6_lsupdate_send_interface, oi, 0,
 | |
| 				 &oi->thread_send_lsupdate);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int ospf6_lsack_send_neighbor(struct thread *thread)
 | |
| {
 | |
| 	struct ospf6_neighbor *on;
 | |
| 	struct ospf6_header *oh;
 | |
| 	uint8_t *p;
 | |
| 	struct ospf6_lsa *lsa;
 | |
| 	int lsa_cnt = 0;
 | |
| 
 | |
| 	on = (struct ospf6_neighbor *)THREAD_ARG(thread);
 | |
| 	on->thread_send_lsack = (struct thread *)NULL;
 | |
| 
 | |
| 	if (on->state < OSPF6_NEIGHBOR_EXCHANGE) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSACK, SEND))
 | |
| 			zlog_debug("Quit to send LSAck to neighbor %s state %s",
 | |
| 				   on->name,
 | |
| 				   ospf6_neighbor_state_str[on->state]);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	/* if we have nothing to send, return */
 | |
| 	if (on->lsack_list->count == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	memset(sendbuf, 0, iobuflen);
 | |
| 	oh = (struct ospf6_header *)sendbuf;
 | |
| 
 | |
| 	p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header));
 | |
| 
 | |
| 	for (ALL_LSDB(on->lsack_list, lsa)) {
 | |
| 		/* MTU check */
 | |
| 		if (p - sendbuf + sizeof(struct ospf6_lsa_header)
 | |
| 		    > ospf6_packet_max(on->ospf6_if)) {
 | |
| 			/* if we run out of packet size/space here,
 | |
| 			   better to try again soon. */
 | |
| 			if (lsa_cnt) {
 | |
| 				oh->type = OSPF6_MESSAGE_TYPE_LSACK;
 | |
| 				oh->length = htons(p - sendbuf);
 | |
| 
 | |
| 				on->ospf6_if->ls_ack_out++;
 | |
| 
 | |
| 				ospf6_send(on->ospf6_if->linklocal_addr,
 | |
| 					   &on->linklocal_addr, on->ospf6_if,
 | |
| 					   oh);
 | |
| 
 | |
| 				memset(sendbuf, 0, iobuflen);
 | |
| 				oh = (struct ospf6_header *)sendbuf;
 | |
| 				p = (uint8_t *)((caddr_t)oh
 | |
| 						+ sizeof(struct ospf6_header));
 | |
| 				lsa_cnt = 0;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
 | |
| 		memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header));
 | |
| 		p += sizeof(struct ospf6_lsa_header);
 | |
| 
 | |
| 		assert(lsa->lock == 2);
 | |
| 		ospf6_lsdb_remove(lsa, on->lsack_list);
 | |
| 		lsa_cnt++;
 | |
| 	}
 | |
| 
 | |
| 	if (lsa_cnt) {
 | |
| 		oh->type = OSPF6_MESSAGE_TYPE_LSACK;
 | |
| 		oh->length = htons(p - sendbuf);
 | |
| 
 | |
| 		on->ospf6_if->ls_ack_out++;
 | |
| 
 | |
| 		ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr,
 | |
| 			   on->ospf6_if, oh);
 | |
| 	}
 | |
| 
 | |
| 	if (on->lsack_list->count > 0)
 | |
| 		thread_add_event(master, ospf6_lsack_send_neighbor, on, 0,
 | |
| 				 &on->thread_send_lsack);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int ospf6_lsack_send_interface(struct thread *thread)
 | |
| {
 | |
| 	struct ospf6_interface *oi;
 | |
| 	struct ospf6_header *oh;
 | |
| 	uint8_t *p;
 | |
| 	struct ospf6_lsa *lsa;
 | |
| 	int lsa_cnt = 0;
 | |
| 
 | |
| 	oi = (struct ospf6_interface *)THREAD_ARG(thread);
 | |
| 	oi->thread_send_lsack = (struct thread *)NULL;
 | |
| 
 | |
| 	if (oi->state <= OSPF6_INTERFACE_WAITING) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSACK, SEND))
 | |
| 			zlog_debug(
 | |
| 				"Quit to send LSAck to interface %s state %s",
 | |
| 				oi->interface->name,
 | |
| 				ospf6_interface_state_str[oi->state]);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	/* if we have nothing to send, return */
 | |
| 	if (oi->lsack_list->count == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	memset(sendbuf, 0, iobuflen);
 | |
| 	oh = (struct ospf6_header *)sendbuf;
 | |
| 
 | |
| 	p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header));
 | |
| 
 | |
| 	for (ALL_LSDB(oi->lsack_list, lsa)) {
 | |
| 		/* MTU check */
 | |
| 		if (p - sendbuf + sizeof(struct ospf6_lsa_header)
 | |
| 		    > ospf6_packet_max(oi)) {
 | |
| 			/* if we run out of packet size/space here,
 | |
| 			   better to try again soon. */
 | |
| 			THREAD_OFF(oi->thread_send_lsack);
 | |
| 			thread_add_event(master, ospf6_lsack_send_interface, oi,
 | |
| 					 0, &oi->thread_send_lsack);
 | |
| 
 | |
| 			ospf6_lsdb_lsa_unlock(lsa);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
 | |
| 		memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header));
 | |
| 		p += sizeof(struct ospf6_lsa_header);
 | |
| 
 | |
| 		assert(lsa->lock == 2);
 | |
| 		ospf6_lsdb_remove(lsa, oi->lsack_list);
 | |
| 		lsa_cnt++;
 | |
| 	}
 | |
| 
 | |
| 	if (lsa_cnt) {
 | |
| 		oh->type = OSPF6_MESSAGE_TYPE_LSACK;
 | |
| 		oh->length = htons(p - sendbuf);
 | |
| 
 | |
| 		if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT)
 | |
| 		    || (oi->state == OSPF6_INTERFACE_DR)
 | |
| 		    || (oi->state == OSPF6_INTERFACE_BDR))
 | |
| 			ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh);
 | |
| 		else
 | |
| 			ospf6_send(oi->linklocal_addr, &alldrouters6, oi, oh);
 | |
| 	}
 | |
| 
 | |
| 	if (oi->lsack_list->count > 0)
 | |
| 		thread_add_event(master, ospf6_lsack_send_interface, oi, 0,
 | |
| 				 &oi->thread_send_lsack);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Commands */
 | |
| DEFUN (debug_ospf6_message,
 | |
|        debug_ospf6_message_cmd,
 | |
|        "debug ospf6 message <unknown|hello|dbdesc|lsreq|lsupdate|lsack|all> [<send|recv>]",
 | |
|        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\n"
 | |
|        "Debug only receiving message\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, "s", 1))
 | |
| 		level = OSPF6_DEBUG_MESSAGE_SEND;
 | |
| 	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>]",
 | |
|        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\n"
 | |
|        "Debug only receiving message\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;
 | |
| 	else if (!strncmp(argv[idx_send_recv]->arg, "s", 1))
 | |
| 		level = OSPF6_DEBUG_MESSAGE_SEND;
 | |
| 	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;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < 6; i++) {
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(i, SEND))
 | |
| 			s |= 1 << i;
 | |
| 		if (IS_OSPF6_DEBUG_MESSAGE(i, RECV))
 | |
| 			r |= 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;
 | |
| 	}
 | |
| 
 | |
| 	/* 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(i, SEND)
 | |
| 		    && IS_OSPF6_DEBUG_MESSAGE(i, RECV))
 | |
| 			vty_out(vty, "debug ospf6 message %s\n", type_str[i]);
 | |
| 		else if (IS_OSPF6_DEBUG_MESSAGE(i, SEND))
 | |
| 			vty_out(vty, "debug ospf6 message %s send\n",
 | |
| 				type_str[i]);
 | |
| 		else if (IS_OSPF6_DEBUG_MESSAGE(i, RECV))
 | |
| 			vty_out(vty, "debug ospf6 message %s recv\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);
 | |
| }
 |