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