/* $OpenBSD: lsack.c,v 1.20 2011/03/25 08:52:21 claudio Exp $ */ /* * Copyright (c) 2004, 2005 Esben Norby * * 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 #include #include #include #include #include #include #include #include "ospfd.h" #include "ospf.h" #include "log.h" #include "ospfe.h" int send_ls_ack(struct iface *, struct in_addr, struct ibuf *); struct ibuf *prepare_ls_ack(struct iface *); void start_ls_ack_tx_timer_now(struct iface *); /* link state acknowledgement packet handling */ struct ibuf * prepare_ls_ack(struct iface *iface) { struct ibuf *buf; if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL) { log_warn("prepare_ls_ack"); return (NULL); } /* OSPF header */ if (gen_ospf_hdr(buf, iface, PACKET_TYPE_LS_ACK)) { log_warn("prepare_ls_ack"); ibuf_free(buf); return (NULL); } return (buf); } int send_ls_ack(struct iface *iface, struct in_addr addr, struct ibuf *buf) { struct sockaddr_in dst; int ret; /* update authentication and calculate checksum */ if (auth_gen(buf, iface)) { log_warn("send_ls_ack"); return (-1); } dst.sin_family = AF_INET; dst.sin_len = sizeof(struct sockaddr_in); dst.sin_addr.s_addr = addr.s_addr; ret = send_packet(iface, buf, &dst); return (ret); } int send_direct_ack(struct iface *iface, struct in_addr addr, void *d, size_t len) { struct ibuf *buf; int ret; if ((buf = prepare_ls_ack(iface)) == NULL) return (-1); /* LS ack(s) */ if (ibuf_add(buf, d, len)) { log_warn("send_direct_ack"); ibuf_free(buf); return (-1); } ret = send_ls_ack(iface, addr, buf); ibuf_free(buf); return (ret); } void recv_ls_ack(struct nbr *nbr, char *buf, u_int16_t len) { struct lsa_hdr lsa_hdr; switch (nbr->state) { case NBR_STA_DOWN: case NBR_STA_ATTEMPT: case NBR_STA_INIT: case NBR_STA_2_WAY: case NBR_STA_XSTRT: case NBR_STA_SNAP: log_debug("recv_ls_ack: packet ignored in state %s, " "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: while (len >= sizeof(lsa_hdr)) { memcpy(&lsa_hdr, buf, sizeof(lsa_hdr)); if (lsa_hdr_check(nbr, &lsa_hdr)) { /* try both list in case of DROTHER */ if (nbr->iface->state & IF_STA_DROTHER) (void)ls_retrans_list_del( nbr->iface->self, &lsa_hdr); (void)ls_retrans_list_del(nbr, &lsa_hdr); } buf += sizeof(lsa_hdr); len -= sizeof(lsa_hdr); } if (len > 0) { log_warnx("recv_ls_ack: bad packet size, " "neighbor ID %s", inet_ntoa(nbr->id)); return; } break; default: fatalx("recv_ls_ack: unknown neighbor state"); } } int lsa_hdr_check(struct nbr *nbr, struct lsa_hdr *lsa_hdr) { /* invalid age */ if ((ntohs(lsa_hdr->age) < 1) || (ntohs(lsa_hdr->age) > MAX_AGE)) { log_debug("lsa_hdr_check: invalid age, neighbor ID %s", inet_ntoa(nbr->id)); return (0); } /* invalid type */ switch (lsa_hdr->type) { case LSA_TYPE_ROUTER: case LSA_TYPE_NETWORK: case LSA_TYPE_SUM_NETWORK: case LSA_TYPE_SUM_ROUTER: case LSA_TYPE_EXTERNAL: break; default: log_debug("lsa_hdr_check: invalid LSA type %d, neighbor ID %s", lsa_hdr->type, inet_ntoa(nbr->id)); return (0); } /* invalid sequence number */ if (ntohl(lsa_hdr->seq_num) == RESV_SEQ_NUM) { log_debug("ls_hdr_check: invalid seq num, neighbor ID %s", inet_ntoa(nbr->id)); return (0); } return (1); } /* link state ack list */ void ls_ack_list_add(struct iface *iface, struct lsa_hdr *lsa) { struct lsa_entry *le; if (lsa == NULL) fatalx("ls_ack_list_add: no LSA header"); if ((le = calloc(1, sizeof(*le))) == NULL) fatal("ls_ack_list_add"); if (ls_ack_list_empty(iface)) start_ls_ack_tx_timer(iface); TAILQ_INSERT_TAIL(&iface->ls_ack_list, le, entry); le->le_lsa = lsa; iface->ls_ack_cnt++; /* reschedule now if we have enough for a reasonably sized packet */ if (iface->ls_ack_cnt > IP_MSS / sizeof(struct lsa_hdr)) start_ls_ack_tx_timer_now(iface); } void ls_ack_list_free(struct iface *iface, struct lsa_entry *le) { TAILQ_REMOVE(&iface->ls_ack_list, le, entry); free(le->le_lsa); free(le); iface->ls_ack_cnt--; } void ls_ack_list_clr(struct iface *iface) { struct lsa_entry *le; while ((le = TAILQ_FIRST(&iface->ls_ack_list)) != NULL) { TAILQ_REMOVE(&iface->ls_ack_list, le, entry); free(le->le_lsa); free(le); } iface->ls_ack_cnt = 0; } int ls_ack_list_empty(struct iface *iface) { return (TAILQ_EMPTY(&iface->ls_ack_list)); } /* timers */ /* ARGSUSED */ void ls_ack_tx_timer(int fd, short event, void *arg) { struct in_addr addr; struct iface *iface = arg; struct lsa_entry *le, *nle; struct nbr *nbr; struct ibuf *buf; int cnt; while (!ls_ack_list_empty(iface)) { if ((buf = prepare_ls_ack(iface)) == NULL) fatal("ls_ack_tx_timer"); cnt = 0; for (le = TAILQ_FIRST(&iface->ls_ack_list); le != NULL; le = nle) { nle = TAILQ_NEXT(le, entry); if (ibuf_left(buf) < sizeof(struct lsa_hdr) + MD5_DIGEST_LENGTH) break; if (ibuf_add(buf, le->le_lsa, sizeof(struct lsa_hdr))) break; ls_ack_list_free(iface, le); cnt++; } if (cnt == 0) { log_warnx("ls_ack_tx_timer: lost in space"); ibuf_free(buf); return; } /* send LS ack(s) but first set correct destination */ switch (iface->type) { case IF_TYPE_POINTOPOINT: inet_aton(AllSPFRouters, &addr); send_ls_ack(iface, addr, buf); break; case IF_TYPE_BROADCAST: if (iface->state & IF_STA_DRORBDR) inet_aton(AllSPFRouters, &addr); else inet_aton(AllDRouters, &addr); send_ls_ack(iface, addr, buf); break; case IF_TYPE_NBMA: case IF_TYPE_POINTOMULTIPOINT: case IF_TYPE_VIRTUALLINK: LIST_FOREACH(nbr, &iface->nbr_list, entry) { if (nbr == iface->self) continue; if (!(nbr->state & NBR_STA_FLOOD)) continue; send_ls_ack(iface, nbr->addr, buf); } break; default: fatalx("lsa_ack_tx_timer: unknown interface type"); } ibuf_free(buf); } } void start_ls_ack_tx_timer(struct iface *iface) { struct timeval tv; timerclear(&tv); tv.tv_sec = iface->rxmt_interval / 2; if (evtimer_add(&iface->lsack_tx_timer, &tv) == -1) fatal("start_ls_ack_tx_timer"); } void start_ls_ack_tx_timer_now(struct iface *iface) { struct timeval tv; timerclear(&tv); if (evtimer_add(&iface->lsack_tx_timer, &tv) == -1) fatal("start_ls_ack_tx_timer_now"); } void stop_ls_ack_tx_timer(struct iface *iface) { if (evtimer_del(&iface->lsack_tx_timer) == -1) fatal("stop_ls_ack_tx_timer"); }