summaryrefslogtreecommitdiff
path: root/usr.sbin/ospfd/database.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/ospfd/database.c')
-rw-r--r--usr.sbin/ospfd/database.c454
1 files changed, 454 insertions, 0 deletions
diff --git a/usr.sbin/ospfd/database.c b/usr.sbin/ospfd/database.c
new file mode 100644
index 00000000000..8d893b75cf1
--- /dev/null
+++ b/usr.sbin/ospfd/database.c
@@ -0,0 +1,454 @@
+/* $OpenBSD: database.c,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <esben.norby@ericsson.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ospfd.h"
+#include "ospf.h"
+#include "log.h"
+#include "ospfe.h"
+
+extern struct ospfd_conf *oeconf;
+
+void db_sum_list_next(struct nbr *);
+
+/* database description packet handling */
+int
+send_db_description(struct nbr *nbr)
+{
+ struct sockaddr_in dst;
+ struct db_dscrp_hdr *dd_hdr;
+ struct lsa_hdr *lsa_hdr;
+ struct lsa_entry *le, *nle;
+ char *buf;
+ char *ptr;
+ int ret = 0;
+
+ log_debug("send_db_description: neighbor ID %s, seq_num %x",
+ inet_ntoa(nbr->id), nbr->dd_seq_num);
+
+ if (nbr->iface->passive)
+ return (0);
+
+ if ((ptr = buf = calloc(1, READ_BUF_SIZE)) == NULL)
+ fatal("send_db_description");
+
+ /* OSPF header */
+ gen_ospf_hdr(ptr, nbr->iface, PACKET_TYPE_DD);
+ ptr += sizeof(struct ospf_hdr);
+
+ /* database description header */
+ dd_hdr = (struct db_dscrp_hdr *)ptr;
+ dd_hdr->opts = oeconf->options;
+ dd_hdr->dd_seq_num = htonl(nbr->dd_seq_num);
+
+ ptr += sizeof(*dd_hdr);
+
+ switch (nbr->state) {
+ case NBR_STA_DOWN:
+ case NBR_STA_ATTEMPT:
+ case NBR_STA_INIT:
+ case NBR_STA_2_WAY:
+ case NBR_STA_SNAP:
+ log_debug("send_db_description: cannot send packet in state %s,"
+ " neighbor ID %s", nbr_state_name(nbr->state),
+ inet_ntoa(nbr->id));
+ ret = -1;
+ goto done;
+ case NBR_STA_XSTRT:
+ log_debug("send_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ nbr->options |= OSPF_DBD_MS | OSPF_DBD_M | OSPF_DBD_I;
+ break;
+ case NBR_STA_XCHNG:
+ log_debug("send_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ if (nbr->master) {
+ /* master */
+ nbr->options |= OSPF_DBD_MS;
+ } else {
+ /* slave */
+ nbr->options &= ~OSPF_DBD_MS;
+ }
+
+ if (TAILQ_EMPTY(&nbr->db_sum_list))
+ nbr->options &= ~OSPF_DBD_M;
+ else
+ nbr->options |= OSPF_DBD_M;
+
+ nbr->options &= ~OSPF_DBD_I;
+
+ /* build LSA list */
+ lsa_hdr = (struct lsa_hdr *)ptr;
+
+ for (le = TAILQ_FIRST(&nbr->db_sum_list); (le != NULL) &&
+ ((ptr - buf) < nbr->iface->mtu - PACKET_HDR); le = nle) {
+ nbr->dd_end = nle = TAILQ_NEXT(le, entry);
+ memcpy(ptr, le->le_lsa, sizeof(struct lsa_hdr));
+ ptr += sizeof(*lsa_hdr);
+ }
+ break;
+ case NBR_STA_LOAD:
+ case NBR_STA_FULL:
+ log_debug("send_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ if (nbr->master) {
+ /* master */
+ nbr->options |= OSPF_DBD_MS;
+ } else {
+ /* slave */
+ nbr->options &= ~OSPF_DBD_MS;
+ }
+ nbr->options &= ~OSPF_DBD_M;
+ nbr->options &= ~OSPF_DBD_I;
+
+ break;
+ default:
+ log_debug("send_db_description: unknown neighbor state, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ ret = -1;
+ goto done;
+ break;
+ }
+
+ /* set destination */
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(struct sockaddr_in);
+
+ switch (nbr->iface->type) {
+ case IF_TYPE_POINTOPOINT:
+ inet_aton(AllSPFRouters, &dst.sin_addr);
+ dd_hdr->iface_mtu = htons(nbr->iface->mtu);
+ break;
+ case IF_TYPE_BROADCAST:
+ dst.sin_addr = nbr->addr;
+ dd_hdr->iface_mtu = htons(nbr->iface->mtu);
+ break;
+ case IF_TYPE_NBMA:
+ case IF_TYPE_POINTOMULTIPOINT:
+ case IF_TYPE_VIRTUALLINK:
+ dst.sin_addr = nbr->addr;
+ dd_hdr->iface_mtu = 0;
+ break;
+ default:
+ fatalx("send_db_description: unknown interface type");
+ }
+
+ dd_hdr->bits = nbr->options;
+
+ /* update authentication and calculate checksum */
+ auth_gen(buf, ptr - buf, nbr->iface);
+
+ /* transmit packet */
+ if ((ret = send_packet(nbr->iface, buf, (ptr - buf), &dst)) == -1)
+ log_warnx("send_db_description: error sending packet on "
+ "interface %s", nbr->iface->name);
+
+done:
+ free(buf);
+
+ return (ret);
+}
+
+void
+recv_db_description(struct nbr *nbr, char *buf, u_int16_t len)
+{
+ struct db_dscrp_hdr dd_hdr;
+ int dupe = 0;
+
+ log_debug("recv_db_description: neighbor ID %s, seq_num %x",
+ inet_ntoa(nbr->id), nbr->dd_seq_num);
+
+ if (len < sizeof(dd_hdr)) {
+ log_warnx("recv_dd_description: "
+ "bad packet size, neighbor ID %s", inet_ntoa(nbr->id));
+ return;
+ }
+ memcpy(&dd_hdr, buf, sizeof(dd_hdr));
+ buf += sizeof(dd_hdr);
+ len -= sizeof(dd_hdr);
+
+
+ /* db description packet sanity checks */
+ if (ntohs(dd_hdr.iface_mtu) != nbr->iface->mtu) {
+ log_warnx("recv_dd_description: invalid MTU, neighbor ID %s",
+ inet_ntoa(nbr->id));
+ return;
+ }
+
+ if (nbr->last_rx_options == dd_hdr.opts &&
+ nbr->last_rx_bits == dd_hdr.bits &&
+ ntohl(dd_hdr.dd_seq_num) == nbr->dd_seq_num - nbr->master ? 1 : 0) {
+ log_debug("recv_db_description: dupe");
+ dupe = 1;
+ }
+
+ log_debug("recv_db_description: seq_num %x", ntohl(dd_hdr.dd_seq_num));
+ switch (nbr->state) {
+ case NBR_STA_DOWN:
+ case NBR_STA_ATTEMPT:
+ case NBR_STA_2_WAY:
+ case NBR_STA_SNAP:
+ log_debug("recv_db_description: packet ignored in state %s, "
+ "neighbor ID %s", nbr_state_name(nbr->state),
+ inet_ntoa(nbr->id));
+ return;
+ case NBR_STA_INIT:
+ log_debug("recv_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ /* evaluate dr and bdr before issuing a 2-Way event */
+ if_fsm(nbr->iface, IF_EVT_NBR_CHNG);
+ nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD);
+ if (nbr->state != NBR_STA_XSTRT)
+ return;
+ /* FALLTHROUGH */
+ case NBR_STA_XSTRT:
+ log_debug("recv_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ if (dupe)
+ return;
+ /*
+ * check bits: either I,M,MS or only M
+ * I,M,MS is checked here the M only case is a fall-through
+ */
+ if (dd_hdr.bits == (OSPF_DBD_I | OSPF_DBD_M | OSPF_DBD_MS)) {
+ /* if nbr Router ID is larger than own -> slave */
+ if ((ntohl(nbr->id.s_addr)) >
+ ntohl(nbr->iface->rtr_id.s_addr)) {
+ /* slave */
+ log_debug("recv_db_description: slave, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ nbr->master = false;
+ nbr->dd_seq_num = ntohl(dd_hdr.dd_seq_num);
+
+ /* event negotiation done */
+ nbr_fsm(nbr, NBR_EVT_NEG_DONE);
+ }
+ } else if (dd_hdr.bits == OSPF_DBD_M) {
+ /* master */
+ if (ntohl(dd_hdr.dd_seq_num) != nbr->dd_seq_num) {
+ log_warnx("recv_db_description: invalid "
+ "seq num, mine %x his %x",
+ nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num));
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+ nbr->dd_seq_num++;
+
+ /* this packet may already have data so pass it on */
+ if (len > 0)
+ ospfe_imsg_compose_rde(IMSG_DD, nbr->peerid,
+ 0, buf, len);
+
+ /* event negotiation done */
+ nbr_fsm(nbr, NBR_EVT_NEG_DONE);
+
+ } else {
+ /* ignore packet */
+ log_debug("recv_db_description: packet ignored in "
+ "state %s (bad flags), neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+ }
+ break;
+ case NBR_STA_XCHNG:
+ case NBR_STA_LOAD:
+ case NBR_STA_FULL:
+ log_debug("recv_db_description: state %s, neighbor ID %s",
+ nbr_state_name(nbr->state), inet_ntoa(nbr->id));
+
+ if (dd_hdr.bits & OSPF_DBD_I ||
+ !(dd_hdr.bits & OSPF_DBD_MS) == !nbr->master) {
+ log_warnx("recv_db_description: seq num mismatch, "
+ "bad flags");
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+
+ if (nbr->last_rx_options != dd_hdr.opts) {
+ log_warnx("recv_db_description: seq num mismatch, "
+ "bad options");
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+
+ if (dupe) {
+ if (!nbr->master)
+ /* retransmit */
+ start_db_tx_timer(nbr);
+ return;
+ }
+
+ if (nbr->state != NBR_STA_XCHNG) {
+ log_warnx("recv_db_description: invalid "
+ "seq num, mine %x his %x",
+ nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num));
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+
+ /* sanity check dd seq number */
+ if (nbr->master) {
+ /* master */
+ if (ntohl(dd_hdr.dd_seq_num) != nbr->dd_seq_num) {
+ log_warnx("recv_db_description: invalid "
+ "seq num, mine %x his %x",
+ nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num));
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+ nbr->dd_seq_num++;
+ } else {
+ /* slave */
+ if (ntohl(dd_hdr.dd_seq_num) != nbr->dd_seq_num + 1) {
+ log_warnx("recv_db_description: invalid "
+ "seq num, mine %x his %x",
+ nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num));
+ nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
+ return;
+ }
+ nbr->dd_seq_num = ntohl(dd_hdr.dd_seq_num);
+ }
+
+ /* forward to RDE and let it decide which LSA's to request */
+ if (len > 0)
+ ospfe_imsg_compose_rde(IMSG_DD, nbr->peerid, 0,
+ buf, len);
+
+ /* next packet */
+ db_sum_list_next(nbr);
+ start_db_tx_timer(nbr);
+
+ if (!(dd_hdr.bits & OSPF_DBD_M) &&
+ TAILQ_EMPTY(&nbr->db_sum_list))
+ nbr_fsm(nbr, NBR_EVT_XCHNG_DONE);
+ break;
+ default:
+ fatalx("recv_db_description: unknown neighbor state");
+ }
+
+ nbr->last_rx_options = dd_hdr.opts;
+ nbr->last_rx_bits = dd_hdr.bits;
+ return;
+}
+
+void
+db_sum_list_add(struct nbr *nbr, struct lsa_hdr *lsa)
+{
+ struct lsa_entry *le;
+
+ if ((le = calloc(1, sizeof(*le))) == NULL)
+ fatal("db_sum_list_add");
+
+ TAILQ_INSERT_TAIL(&nbr->db_sum_list, le, entry);
+ le->le_lsa = lsa;
+}
+
+void
+db_sum_list_next(struct nbr *nbr)
+{
+ struct lsa_entry *le;
+
+ while ((le = TAILQ_FIRST(&nbr->db_sum_list)) != nbr->dd_end) {
+ TAILQ_REMOVE(&nbr->db_sum_list, le, entry);
+ free(le->le_lsa);
+ free(le);
+ }
+}
+
+void
+db_sum_list_clr(struct nbr *nbr)
+{
+ nbr->dd_end = NULL;
+ db_sum_list_next(nbr);
+}
+
+/* timers */
+void
+db_tx_timer(int fd, short event, void *arg)
+{
+ struct nbr *nbr = arg;
+ struct timeval tv;
+
+ log_debug("db_tx_timer: neighbor ID %s", inet_ntoa(nbr->id));
+
+ switch (nbr->state) {
+ case NBR_STA_DOWN:
+ case NBR_STA_ATTEMPT:
+ case NBR_STA_INIT:
+ case NBR_STA_2_WAY:
+ case NBR_STA_SNAP:
+ case NBR_STA_LOAD:
+ return ;
+ case NBR_STA_XSTRT:
+ case NBR_STA_XCHNG:
+ case NBR_STA_FULL:
+ send_db_description(nbr);
+ break;
+ default:
+ log_debug("db_tx_timer: unknown neighbor state, "
+ "neighbor ID %s", inet_ntoa(nbr->id));
+ break;
+ }
+
+ /* reschedule db_tx_timer but only in master mode */
+ if (nbr->master) {
+ timerclear(&tv);
+ tv.tv_sec = nbr->iface->rxmt_interval;
+ log_debug("db_tx_timer: reschedule neighbor ID %s",
+ inet_ntoa(nbr->id));
+ evtimer_add(&nbr->db_tx_timer, &tv);
+ }
+}
+
+int
+start_db_tx_timer(struct nbr *nbr)
+{
+ struct timeval tv;
+
+ if (nbr == nbr->iface->self)
+ return (0);
+
+ log_debug("start_db_tx_timer: neighbor ID %s", inet_ntoa(nbr->id));
+
+ timerclear(&tv);
+
+ return (evtimer_add(&nbr->db_tx_timer, &tv));
+}
+
+int
+stop_db_tx_timer(struct nbr *nbr)
+{
+ if (nbr == nbr->iface->self)
+ return (0);
+
+ log_debug("stop_db_tx_timer: neighbor ID %s", inet_ntoa(nbr->id));
+
+ return (evtimer_del(&nbr->db_tx_timer));
+}