diff options
| author | 2007-10-08 10:44:50 +0000 | |
|---|---|---|
| committer | 2007-10-08 10:44:50 +0000 | |
| commit | a1a4e97b0cce230734864cbdea30a16973fa6d4f (patch) | |
| tree | 5fd8c56d912b65acf54e6e27ba7b7c3f5d5ab9bc /usr.sbin/ospf6d/database.c | |
| parent | regen (diff) | |
| download | wireguard-openbsd-a1a4e97b0cce230734864cbdea30a16973fa6d4f.tar.xz wireguard-openbsd-a1a4e97b0cce230734864cbdea30a16973fa6d4f.zip | |
Welcome ospf6d
The new ospf6d daemon will support OSPFv3, basically OSPF for IPv6 networks.
It is heavily based on ospfd(8), it is more or less a copy and paste of it.
Currently some unneeded stuff has been removed and the trasition from
IPv4 to IPv6 has begun.
ospf6d is not very usefull at the moment, it is being imported to allow more
people to work on it concurrently.
Not yet connected to the builds.
ok claudio@ dlg@
Diffstat (limited to 'usr.sbin/ospf6d/database.c')
| -rw-r--r-- | usr.sbin/ospf6d/database.c | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/usr.sbin/ospf6d/database.c b/usr.sbin/ospf6d/database.c new file mode 100644 index 00000000000..955bc469b40 --- /dev/null +++ b/usr.sbin/ospf6d/database.c @@ -0,0 +1,422 @@ +/* $OpenBSD: database.c,v 1.1 2007/10/08 10:44:50 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2007 Esben Norby <norby@openbsd.org> + * + * 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 <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ospf6d.h" +#include "ospf6.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_in6 dst; + struct db_dscrp_hdr dd_hdr; + struct lsa_entry *le, *nle; + struct buf *buf; + int ret = 0; + + if ((buf = buf_open(nbr->iface->mtu - sizeof(struct ip))) == NULL) + fatal("send_db_description"); + + /* OSPF header */ + if (gen_ospf_hdr(buf, nbr->iface, PACKET_TYPE_DD)) + goto fail; + + /* reserve space for database description header */ + if (buf_reserve(buf, sizeof(dd_hdr)) == NULL) + goto fail; + + 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: + nbr->options |= OSPF_DBD_MS | OSPF_DBD_M | OSPF_DBD_I; + break; + case NBR_STA_XCHNG: + 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, keep space for a possible md5 sum */ + for (le = TAILQ_FIRST(&nbr->db_sum_list); le != NULL && + buf->wpos + sizeof(struct lsa_hdr) < buf->max - + MD5_DIGEST_LENGTH; le = nle) { + nbr->dd_end = nle = TAILQ_NEXT(le, entry); + if (buf_add(buf, le->le_lsa, sizeof(struct lsa_hdr))) + goto fail; + } + break; + case NBR_STA_LOAD: + case NBR_STA_FULL: + 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: + fatalx("send_db_description: unknown neighbor state"); + } + + /* set destination */ + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(struct sockaddr_in6); + + switch (nbr->iface->type) { + case IF_TYPE_POINTOPOINT: + inet_pton(AF_INET6, AllSPFRouters, &dst.sin6_addr); + dd_hdr.iface_mtu = htons(nbr->iface->mtu); + break; + case IF_TYPE_BROADCAST: + dst.sin6_addr = nbr->addr; + dd_hdr.iface_mtu = htons(nbr->iface->mtu); + break; + case IF_TYPE_NBMA: + case IF_TYPE_POINTOMULTIPOINT: + /* XXX not supported */ + break; + case IF_TYPE_VIRTUALLINK: + dst.sin6_addr = nbr->iface->dst; + dd_hdr.iface_mtu = 0; + break; + default: + fatalx("send_db_description: unknown interface type"); + } + + dd_hdr.opts = oeconf->options; + dd_hdr.bits = nbr->options; + dd_hdr.dd_seq_num = htonl(nbr->dd_seq_num); + + memcpy(buf_seek(buf, sizeof(struct ospf_hdr), sizeof(dd_hdr)), + &dd_hdr, sizeof(dd_hdr)); + + /* calculate checksum */ + if (upd_ospf_hdr(buf, nbr->iface)) + goto fail; + + /* transmit packet */ + ret = send_packet(nbr->iface, buf->buf, buf->wpos, &dst); +done: + buf_free(buf); + return (ret); +fail: + log_warn("send_db_description"); + buf_free(buf); + return (-1); +} + +void +recv_db_description(struct nbr *nbr, char *buf, u_int16_t len) +{ + struct db_dscrp_hdr dd_hdr; + int dupe = 0; + + 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 %d sent by " + "neighbor ID %s, expected %d", ntohs(dd_hdr.iface_mtu), + inet_ntoa(nbr->id), nbr->iface->mtu); + 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; + } + + 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: + /* evaluate dr and bdr after issuing a 2-Way event */ + nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD); + if_fsm(nbr->iface, IF_EVT_NBR_CHNG); + if (nbr->state != NBR_STA_XSTRT) + return; + /* FALLTHROUGH */ + case NBR_STA_XSTRT: + if (dupe) + return; + /* + * check bits: either I,M,MS or only M + */ + 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(ospfe_router_id())) { + /* slave */ + nbr->master = 0; + 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_I | OSPF_DBD_MS))) { + /* M only case: we are 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) { + nbr->dd_pending++; + 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: + 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 LSAs to request */ + if (len > 0) { + nbr->dd_pending++; + 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)) + if (!nbr->master || !(nbr->options & OSPF_DBD_M)) + 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; +} + +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 */ +/* ARGSUSED */ +void +db_tx_timer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + struct timeval tv; + + switch (nbr->state) { + case NBR_STA_DOWN: + case NBR_STA_ATTEMPT: + case NBR_STA_INIT: + case NBR_STA_2_WAY: + case NBR_STA_SNAP: + return ; + case NBR_STA_XSTRT: + case NBR_STA_XCHNG: + case NBR_STA_LOAD: + 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; + if (evtimer_add(&nbr->db_tx_timer, &tv) == -1) + fatal("db_tx_timer"); + } +} + +void +start_db_tx_timer(struct nbr *nbr) +{ + struct timeval tv; + + if (nbr == nbr->iface->self) + return; + + timerclear(&tv); + if (evtimer_add(&nbr->db_tx_timer, &tv) == -1) + fatal("start_db_tx_timer"); +} + +void +stop_db_tx_timer(struct nbr *nbr) +{ + if (nbr == nbr->iface->self) + return; + + if (evtimer_del(&nbr->db_tx_timer) == -1) + fatal("stop_db_tx_timer"); +} |
