Edit

IABSD.fr/src/usr.sbin/ospfd/database.c

Branch :

  • Show log

    Commit

  • Author : florian
    Date : 2024-08-21 15:18:00
    Hash : 4f4fe40b
    Message : Mechanically replace inet_aton with inet_pton. OK claudio, deraadt

  • usr.sbin/ospfd/database.c
  • /*	$OpenBSD: database.c,v 1.38 2024/08/21 15:18:00 florian Exp $ */
    
    /*
     * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
     * Copyright (c) 2004, 2005 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/ip.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_entry	*le, *nle;
    	struct ibuf		*buf;
    	u_int8_t		 bits = 0;
    
    	if ((buf = ibuf_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 (ibuf_add_zero(buf, sizeof(dd_hdr)) == -1)
    		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: neighbor ID %s (%s): "
    		    "cannot send packet in state %s", inet_ntoa(nbr->id),
    		    nbr->iface->name, nbr_state_name(nbr->state));
    		goto fail;
    	case NBR_STA_XSTRT:
    		bits |= OSPF_DBD_MS | OSPF_DBD_M | OSPF_DBD_I;
    		nbr->dd_more = 1;
    		break;
    	case NBR_STA_XCHNG:
    		if (nbr->dd_master)
    			bits |= OSPF_DBD_MS;
    		else
    			bits &= ~OSPF_DBD_MS;
    
    		if (TAILQ_EMPTY(&nbr->db_sum_list)) {
    			bits &= ~OSPF_DBD_M;
    			nbr->dd_more = 0;
    		} else {
    			bits |= OSPF_DBD_M;
    			nbr->dd_more = 1;
    		}
    
    		bits &= ~OSPF_DBD_I;
    
    		/* build LSA list, keep space for a possible md5 sum */
    		for (le = TAILQ_FIRST(&nbr->db_sum_list); le != NULL &&
    		    ibuf_left(buf) >= MD5_DIGEST_LENGTH + sizeof(struct lsa_hdr);
    		    le = nle) {
    			nbr->dd_end = nle = TAILQ_NEXT(le, entry);
    			if (ibuf_add(buf, le->le_lsa, sizeof(struct lsa_hdr)))
    				goto fail;
    		}
    		break;
    	case NBR_STA_LOAD:
    	case NBR_STA_FULL:
    		if (nbr->dd_master)
    			bits |= OSPF_DBD_MS;
    		else
    			bits &= ~OSPF_DBD_MS;
    		bits &= ~OSPF_DBD_M;
    		bits &= ~OSPF_DBD_I;
    
    		nbr->dd_more = 0;
    		break;
    	default:
    		fatalx("send_db_description: unknown neighbor state");
    	}
    
    	/* set destination */
    	dst.sin_family = AF_INET;
    	dst.sin_len = sizeof(struct sockaddr_in);
    
    	switch (nbr->iface->type) {
    	case IF_TYPE_POINTOPOINT:
    		inet_pton(AF_INET, 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:
    		/* XXX not supported */
    		break;
    	case IF_TYPE_VIRTUALLINK:
    		dst.sin_addr = nbr->iface->dst;
    		dd_hdr.iface_mtu = 0;
    		break;
    	default:
    		fatalx("send_db_description: unknown interface type");
    	}
    
    	/* XXX button or not for opaque LSA? */
    	dd_hdr.opts = area_ospf_options(nbr->iface->area) | OSPF_OPTION_O;
    	dd_hdr.bits = bits;
    	dd_hdr.dd_seq_num = htonl(nbr->dd_seq_num);
    
    	if (ibuf_set(buf, sizeof(struct ospf_hdr), &dd_hdr,
    	    sizeof(dd_hdr)) == -1)
    		goto fail;
    
    	/* update authentication and calculate checksum */
    	if (auth_gen(buf, nbr->iface))
    		goto fail;
    
    	/* transmit packet */
    	if (send_packet(nbr->iface, buf, &dst) == -1)
    		goto fail;
    
    	ibuf_free(buf);
    	return (0);
    fail:
    	log_warn("%s", __func__);
    	ibuf_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_db_description: neighbor ID %s (%s): "
    		    "bad packet size", inet_ntoa(nbr->id), nbr->iface->name);
    		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_db_description: neighbor ID %s (%s): "
    		    "invalid MTU %d expected %d", inet_ntoa(nbr->id),
    		    nbr->iface->name, ntohs(dd_hdr.iface_mtu),
    		    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->dd_master ?
    	    1 : 0) {
    		log_debug("recv_db_description: dupe from "
    		    "neighbor ID %s (%s)", inet_ntoa(nbr->id),
    		    nbr->iface->name);
    		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: neighbor ID %s (%s): "
    		    "packet ignored in state %s", inet_ntoa(nbr->id),
    		    nbr->iface->name, nbr_state_name(nbr->state));
    		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;
    		nbr->capa_options = dd_hdr.opts;
    		if ((nbr->capa_options & nbr->options) != nbr->options) {
    			log_warnx("recv_db_description: neighbor ID %s (%s) "
    			    "sent inconsistent options %x vs. %x",
    			    inet_ntoa(nbr->id), nbr->iface->name,
    			    nbr->capa_options, nbr->options);
    		}
    		/*
    		 * 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->dd_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: "
    				    "neighbor ID %s (%s): "
    				    "invalid seq num, mine %x his %x",
    				    inet_ntoa(nbr->id), nbr->iface->name,
    				    nbr->dd_seq_num, ntohl(dd_hdr.dd_seq_num));
    				return;
    			}
    			nbr->dd_seq_num++;
    
    			/* event negotiation done */
    			nbr_fsm(nbr, NBR_EVT_NEG_DONE);
    
    			/* 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);
    			}
    		} else {
    			/* ignore packet */
    			log_debug("recv_db_description: neighbor ID %s (%s): "
    			    "packet ignored in state %s (bad flags)",
    			    inet_ntoa(nbr->id), nbr->iface->name,
    			    nbr_state_name(nbr->state));
    		}
    		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->dd_master) {
    			log_warnx("recv_db_description: neighbor ID %s (%s): "
    			    "seq num mismatch, bad flags", inet_ntoa(nbr->id),
    			    nbr->iface->name);
    			nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
    			return;
    		}
    
    		if (nbr->last_rx_options != dd_hdr.opts) {
    			log_warnx("recv_db_description: neighbor ID %s (%s): "
    			    "seq num mismatch, bad options",
    			    inet_ntoa(nbr->id), nbr->iface->name);
    			nbr_fsm(nbr, NBR_EVT_SEQ_NUM_MIS);
    			return;
    		}
    
    		if (dupe) {
    			if (!nbr->dd_master)
    				/* retransmit */
    				start_db_tx_timer(nbr);
    			return;
    		}
    
    		if (nbr->state != NBR_STA_XCHNG) {
    			log_warnx("recv_db_description: neighbor ID %s (%s): "
    			    "invalid seq num, mine %x his %x",
    			    inet_ntoa(nbr->id), nbr->iface->name,
    			    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->dd_master) {
    			/* master */
    			if (ntohl(dd_hdr.dd_seq_num) != nbr->dd_seq_num) {
    				log_warnx("recv_db_description: "
    				    "neighbor ID %s (%s): "
    				    "invalid seq num, mine %x his %x, master",
    				    inet_ntoa(nbr->id), nbr->iface->name,
    				    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: "
    				    "neighbor ID %s (%s): "
    				    "invalid seq num, mine %x his %x, slave",
    				    inet_ntoa(nbr->id), nbr->iface->name,
    				    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->dd_master || !nbr->dd_more)
    				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 */
    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: neighbor ID %s (%s): "
    		    "unknown neighbor state",
    		    inet_ntoa(nbr->id), nbr->iface->name);
    		break;
    	}
    
    	/* reschedule db_tx_timer but only in master mode */
    	if (nbr->dd_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");
    }