1*4f4fe40bSflorian /* $OpenBSD: lsack.c,v 1.25 2024/08/21 15:18:00 florian Exp $ */ 2204df0f8Sclaudio 3204df0f8Sclaudio /* 4367f601bSnorby * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 5204df0f8Sclaudio * 6204df0f8Sclaudio * Permission to use, copy, modify, and distribute this software for any 7204df0f8Sclaudio * purpose with or without fee is hereby granted, provided that the above 8204df0f8Sclaudio * copyright notice and this permission notice appear in all copies. 9204df0f8Sclaudio * 10204df0f8Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11204df0f8Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12204df0f8Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13204df0f8Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14204df0f8Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15204df0f8Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16204df0f8Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17204df0f8Sclaudio */ 18204df0f8Sclaudio 19204df0f8Sclaudio #include <sys/types.h> 20204df0f8Sclaudio #include <sys/socket.h> 21204df0f8Sclaudio #include <netinet/in.h> 22204df0f8Sclaudio #include <netinet/ip.h> 23204df0f8Sclaudio #include <arpa/inet.h> 24204df0f8Sclaudio 25204df0f8Sclaudio #include <stdlib.h> 2653e1908aSstevesk #include <string.h> 27204df0f8Sclaudio 28204df0f8Sclaudio #include "ospfd.h" 29204df0f8Sclaudio #include "ospf.h" 30204df0f8Sclaudio #include "log.h" 31204df0f8Sclaudio #include "ospfe.h" 32204df0f8Sclaudio 3362d04914Sclaudio int send_ls_ack(struct iface *, struct in_addr, struct ibuf *); 3462d04914Sclaudio struct ibuf *prepare_ls_ack(struct iface *); 353aede346Sclaudio void start_ls_ack_tx_timer_now(struct iface *); 36204df0f8Sclaudio 37204df0f8Sclaudio /* link state acknowledgement packet handling */ 3862d04914Sclaudio struct ibuf * 3962d04914Sclaudio prepare_ls_ack(struct iface *iface) 4062d04914Sclaudio { 4162d04914Sclaudio struct ibuf *buf; 4262d04914Sclaudio 4362d04914Sclaudio if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL) { 4462d04914Sclaudio log_warn("prepare_ls_ack"); 4562d04914Sclaudio return (NULL); 4662d04914Sclaudio } 4762d04914Sclaudio 4862d04914Sclaudio /* OSPF header */ 4962d04914Sclaudio if (gen_ospf_hdr(buf, iface, PACKET_TYPE_LS_ACK)) { 5062d04914Sclaudio log_warn("prepare_ls_ack"); 5162d04914Sclaudio ibuf_free(buf); 5262d04914Sclaudio return (NULL); 5362d04914Sclaudio } 5462d04914Sclaudio 5562d04914Sclaudio return (buf); 5662d04914Sclaudio } 5762d04914Sclaudio 58204df0f8Sclaudio int 5962d04914Sclaudio send_ls_ack(struct iface *iface, struct in_addr addr, struct ibuf *buf) 60204df0f8Sclaudio { 61204df0f8Sclaudio struct sockaddr_in dst; 62204df0f8Sclaudio 6362d04914Sclaudio /* update authentication and calculate checksum */ 6462d04914Sclaudio if (auth_gen(buf, iface)) { 6562d04914Sclaudio log_warn("send_ls_ack"); 6662d04914Sclaudio return (-1); 6762d04914Sclaudio } 68204df0f8Sclaudio 69204df0f8Sclaudio dst.sin_family = AF_INET; 70204df0f8Sclaudio dst.sin_len = sizeof(struct sockaddr_in); 71204df0f8Sclaudio dst.sin_addr.s_addr = addr.s_addr; 72204df0f8Sclaudio 7325a742ccSremi if (send_packet(iface, buf, &dst) == -1) { 7425a742ccSremi log_warn("%s", __func__); 7525a742ccSremi return (-1); 7625a742ccSremi } 7725a742ccSremi return (0); 7862d04914Sclaudio } 7962d04914Sclaudio 8062d04914Sclaudio int 8162d04914Sclaudio send_direct_ack(struct iface *iface, struct in_addr addr, void *d, size_t len) 8262d04914Sclaudio { 8362d04914Sclaudio struct ibuf *buf; 8462d04914Sclaudio int ret; 8562d04914Sclaudio 8662d04914Sclaudio if ((buf = prepare_ls_ack(iface)) == NULL) 8762d04914Sclaudio return (-1); 88204df0f8Sclaudio 89204df0f8Sclaudio /* LS ack(s) */ 9062d04914Sclaudio if (ibuf_add(buf, d, len)) { 9162d04914Sclaudio log_warn("send_direct_ack"); 92e39620e5Snicm ibuf_free(buf); 930491ce75Sclaudio return (-1); 94204df0f8Sclaudio } 95204df0f8Sclaudio 9662d04914Sclaudio ret = send_ls_ack(iface, addr, buf); 9762d04914Sclaudio ibuf_free(buf); 9862d04914Sclaudio return (ret); 9962d04914Sclaudio } 10062d04914Sclaudio 101204df0f8Sclaudio void 102204df0f8Sclaudio recv_ls_ack(struct nbr *nbr, char *buf, u_int16_t len) 103204df0f8Sclaudio { 104204df0f8Sclaudio struct lsa_hdr lsa_hdr; 105204df0f8Sclaudio 106204df0f8Sclaudio switch (nbr->state) { 107204df0f8Sclaudio case NBR_STA_DOWN: 108204df0f8Sclaudio case NBR_STA_ATTEMPT: 109204df0f8Sclaudio case NBR_STA_INIT: 110204df0f8Sclaudio case NBR_STA_2_WAY: 111204df0f8Sclaudio case NBR_STA_XSTRT: 112204df0f8Sclaudio case NBR_STA_SNAP: 113204df0f8Sclaudio log_debug("recv_ls_ack: packet ignored in state %s, " 1149cc19ef1Ssthen "neighbor ID %s (%s)", nbr_state_name(nbr->state), 1159cc19ef1Ssthen inet_ntoa(nbr->id), nbr->iface->name); 116204df0f8Sclaudio break; 117204df0f8Sclaudio case NBR_STA_XCHNG: 118204df0f8Sclaudio case NBR_STA_LOAD: 119204df0f8Sclaudio case NBR_STA_FULL: 120204df0f8Sclaudio while (len >= sizeof(lsa_hdr)) { 121204df0f8Sclaudio memcpy(&lsa_hdr, buf, sizeof(lsa_hdr)); 122204df0f8Sclaudio 123cb75af8bSclaudio if (lsa_hdr_check(nbr, &lsa_hdr)) { 124cb75af8bSclaudio /* try both list in case of DROTHER */ 125cb75af8bSclaudio if (nbr->iface->state & IF_STA_DROTHER) 1263aede346Sclaudio (void)ls_retrans_list_del( 1273aede346Sclaudio nbr->iface->self, &lsa_hdr); 1283aede346Sclaudio (void)ls_retrans_list_del(nbr, &lsa_hdr); 129cb75af8bSclaudio } 130204df0f8Sclaudio 131204df0f8Sclaudio buf += sizeof(lsa_hdr); 132204df0f8Sclaudio len -= sizeof(lsa_hdr); 133204df0f8Sclaudio } 134204df0f8Sclaudio if (len > 0) { 135204df0f8Sclaudio log_warnx("recv_ls_ack: bad packet size, " 1369cc19ef1Ssthen "neighbor ID %s (%s)", inet_ntoa(nbr->id), 1379cc19ef1Ssthen nbr->iface->name); 138204df0f8Sclaudio return; 139204df0f8Sclaudio } 140204df0f8Sclaudio break; 141204df0f8Sclaudio default: 142204df0f8Sclaudio fatalx("recv_ls_ack: unknown neighbor state"); 143204df0f8Sclaudio } 144204df0f8Sclaudio } 145204df0f8Sclaudio 146204df0f8Sclaudio int 147204df0f8Sclaudio lsa_hdr_check(struct nbr *nbr, struct lsa_hdr *lsa_hdr) 148204df0f8Sclaudio { 149204df0f8Sclaudio /* invalid age */ 150204df0f8Sclaudio if ((ntohs(lsa_hdr->age) < 1) || (ntohs(lsa_hdr->age) > MAX_AGE)) { 1519cc19ef1Ssthen log_debug("lsa_hdr_check: invalid age, neighbor ID %s (%s)", 1529cc19ef1Ssthen inet_ntoa(nbr->id), nbr->iface->name); 153204df0f8Sclaudio return (0); 154204df0f8Sclaudio } 155204df0f8Sclaudio 156204df0f8Sclaudio /* invalid type */ 157204df0f8Sclaudio switch (lsa_hdr->type) { 158204df0f8Sclaudio case LSA_TYPE_ROUTER: 159204df0f8Sclaudio case LSA_TYPE_NETWORK: 160204df0f8Sclaudio case LSA_TYPE_SUM_NETWORK: 161204df0f8Sclaudio case LSA_TYPE_SUM_ROUTER: 162204df0f8Sclaudio case LSA_TYPE_EXTERNAL: 163204df0f8Sclaudio break; 164204df0f8Sclaudio default: 1659cc19ef1Ssthen log_debug("lsa_hdr_check: invalid LSA type %d, " 1669cc19ef1Ssthen "neighbor ID %s (%s)", 1679cc19ef1Ssthen lsa_hdr->type, inet_ntoa(nbr->id), nbr->iface->name); 168204df0f8Sclaudio return (0); 169204df0f8Sclaudio } 170204df0f8Sclaudio 171204df0f8Sclaudio /* invalid sequence number */ 172fd136cabSclaudio if (ntohl(lsa_hdr->seq_num) == RESV_SEQ_NUM) { 1739cc19ef1Ssthen log_debug("ls_hdr_check: invalid seq num, " 1749cc19ef1Ssthen "neighbor ID %s (%s)", inet_ntoa(nbr->id), 1759cc19ef1Ssthen nbr->iface->name); 176204df0f8Sclaudio return (0); 177204df0f8Sclaudio } 178204df0f8Sclaudio 179204df0f8Sclaudio return (1); 180204df0f8Sclaudio } 181204df0f8Sclaudio 182204df0f8Sclaudio /* link state ack list */ 183204df0f8Sclaudio void 184204df0f8Sclaudio ls_ack_list_add(struct iface *iface, struct lsa_hdr *lsa) 185204df0f8Sclaudio { 186204df0f8Sclaudio struct lsa_entry *le; 187204df0f8Sclaudio 188204df0f8Sclaudio if (lsa == NULL) 189204df0f8Sclaudio fatalx("ls_ack_list_add: no LSA header"); 190204df0f8Sclaudio 191204df0f8Sclaudio if ((le = calloc(1, sizeof(*le))) == NULL) 192204df0f8Sclaudio fatal("ls_ack_list_add"); 193204df0f8Sclaudio 194204df0f8Sclaudio if (ls_ack_list_empty(iface)) 195204df0f8Sclaudio start_ls_ack_tx_timer(iface); 196204df0f8Sclaudio 197204df0f8Sclaudio TAILQ_INSERT_TAIL(&iface->ls_ack_list, le, entry); 198204df0f8Sclaudio le->le_lsa = lsa; 199204df0f8Sclaudio iface->ls_ack_cnt++; 200204df0f8Sclaudio 20162d04914Sclaudio /* reschedule now if we have enough for a reasonably sized packet */ 20262d04914Sclaudio if (iface->ls_ack_cnt > IP_MSS / sizeof(struct lsa_hdr)) 203204df0f8Sclaudio start_ls_ack_tx_timer_now(iface); 204204df0f8Sclaudio } 205204df0f8Sclaudio 206204df0f8Sclaudio void 207204df0f8Sclaudio ls_ack_list_free(struct iface *iface, struct lsa_entry *le) 208204df0f8Sclaudio { 209204df0f8Sclaudio TAILQ_REMOVE(&iface->ls_ack_list, le, entry); 210204df0f8Sclaudio free(le->le_lsa); 211204df0f8Sclaudio free(le); 212204df0f8Sclaudio 213204df0f8Sclaudio iface->ls_ack_cnt--; 214204df0f8Sclaudio } 215204df0f8Sclaudio 216204df0f8Sclaudio void 217204df0f8Sclaudio ls_ack_list_clr(struct iface *iface) 218204df0f8Sclaudio { 219204df0f8Sclaudio struct lsa_entry *le; 220204df0f8Sclaudio 221204df0f8Sclaudio while ((le = TAILQ_FIRST(&iface->ls_ack_list)) != NULL) { 222204df0f8Sclaudio TAILQ_REMOVE(&iface->ls_ack_list, le, entry); 223204df0f8Sclaudio free(le->le_lsa); 224204df0f8Sclaudio free(le); 225204df0f8Sclaudio } 226204df0f8Sclaudio iface->ls_ack_cnt = 0; 227204df0f8Sclaudio } 228204df0f8Sclaudio 2295c6e55e9Snorby int 230204df0f8Sclaudio ls_ack_list_empty(struct iface *iface) 231204df0f8Sclaudio { 232204df0f8Sclaudio return (TAILQ_EMPTY(&iface->ls_ack_list)); 233204df0f8Sclaudio } 234204df0f8Sclaudio 235204df0f8Sclaudio /* timers */ 236204df0f8Sclaudio void 237204df0f8Sclaudio ls_ack_tx_timer(int fd, short event, void *arg) 238204df0f8Sclaudio { 239204df0f8Sclaudio struct in_addr addr; 240204df0f8Sclaudio struct iface *iface = arg; 241204df0f8Sclaudio struct lsa_entry *le, *nle; 242204df0f8Sclaudio struct nbr *nbr; 24362d04914Sclaudio struct ibuf *buf; 24462d04914Sclaudio int cnt; 245204df0f8Sclaudio 246204df0f8Sclaudio while (!ls_ack_list_empty(iface)) { 24762d04914Sclaudio if ((buf = prepare_ls_ack(iface)) == NULL) 24862d04914Sclaudio fatal("ls_ack_tx_timer"); 249204df0f8Sclaudio cnt = 0; 25062d04914Sclaudio 25162d04914Sclaudio for (le = TAILQ_FIRST(&iface->ls_ack_list); le != NULL; 25262d04914Sclaudio le = nle) { 253204df0f8Sclaudio nle = TAILQ_NEXT(le, entry); 25462d04914Sclaudio if (ibuf_left(buf) < sizeof(struct lsa_hdr) + 25562d04914Sclaudio MD5_DIGEST_LENGTH) 25662d04914Sclaudio break; 25762d04914Sclaudio if (ibuf_add(buf, le->le_lsa, sizeof(struct lsa_hdr))) 25862d04914Sclaudio break; 259204df0f8Sclaudio ls_ack_list_free(iface, le); 260204df0f8Sclaudio cnt++; 261204df0f8Sclaudio } 26262d04914Sclaudio if (cnt == 0) { 26362d04914Sclaudio log_warnx("ls_ack_tx_timer: lost in space"); 26462d04914Sclaudio ibuf_free(buf); 26562d04914Sclaudio return; 26662d04914Sclaudio } 267204df0f8Sclaudio 268204df0f8Sclaudio /* send LS ack(s) but first set correct destination */ 269204df0f8Sclaudio switch (iface->type) { 270204df0f8Sclaudio case IF_TYPE_POINTOPOINT: 271*4f4fe40bSflorian inet_pton(AF_INET, AllSPFRouters, &addr); 27262d04914Sclaudio send_ls_ack(iface, addr, buf); 273204df0f8Sclaudio break; 274204df0f8Sclaudio case IF_TYPE_BROADCAST: 275204df0f8Sclaudio if (iface->state & IF_STA_DRORBDR) 276*4f4fe40bSflorian inet_pton(AF_INET, AllSPFRouters, &addr); 277204df0f8Sclaudio else 278*4f4fe40bSflorian inet_pton(AF_INET, AllDRouters, &addr); 27962d04914Sclaudio send_ls_ack(iface, addr, buf); 280204df0f8Sclaudio break; 281204df0f8Sclaudio case IF_TYPE_NBMA: 282204df0f8Sclaudio case IF_TYPE_POINTOMULTIPOINT: 283204df0f8Sclaudio case IF_TYPE_VIRTUALLINK: 284204df0f8Sclaudio LIST_FOREACH(nbr, &iface->nbr_list, entry) { 285204df0f8Sclaudio if (nbr == iface->self) 286204df0f8Sclaudio continue; 287204df0f8Sclaudio if (!(nbr->state & NBR_STA_FLOOD)) 288204df0f8Sclaudio continue; 28962d04914Sclaudio send_ls_ack(iface, nbr->addr, buf); 290204df0f8Sclaudio } 291204df0f8Sclaudio break; 292204df0f8Sclaudio default: 293db06c020Snorby fatalx("lsa_ack_tx_timer: unknown interface type"); 294204df0f8Sclaudio } 29562d04914Sclaudio ibuf_free(buf); 296204df0f8Sclaudio } 297204df0f8Sclaudio } 298204df0f8Sclaudio 2993aede346Sclaudio void 300204df0f8Sclaudio start_ls_ack_tx_timer(struct iface *iface) 301204df0f8Sclaudio { 302204df0f8Sclaudio struct timeval tv; 303204df0f8Sclaudio 304204df0f8Sclaudio timerclear(&tv); 305204df0f8Sclaudio tv.tv_sec = iface->rxmt_interval / 2; 3064fafafe9Snorby 3073aede346Sclaudio if (evtimer_add(&iface->lsack_tx_timer, &tv) == -1) 3083aede346Sclaudio fatal("start_ls_ack_tx_timer"); 309204df0f8Sclaudio } 310204df0f8Sclaudio 3113aede346Sclaudio void 312204df0f8Sclaudio start_ls_ack_tx_timer_now(struct iface *iface) 313204df0f8Sclaudio { 314204df0f8Sclaudio struct timeval tv; 315204df0f8Sclaudio 316204df0f8Sclaudio timerclear(&tv); 3173aede346Sclaudio if (evtimer_add(&iface->lsack_tx_timer, &tv) == -1) 3183aede346Sclaudio fatal("start_ls_ack_tx_timer_now"); 319204df0f8Sclaudio } 320204df0f8Sclaudio 3213aede346Sclaudio void 322204df0f8Sclaudio stop_ls_ack_tx_timer(struct iface *iface) 323204df0f8Sclaudio { 3243aede346Sclaudio if (evtimer_del(&iface->lsack_tx_timer) == -1) 3253aede346Sclaudio fatal("stop_ls_ack_tx_timer"); 326204df0f8Sclaudio } 327