1*4f4fe40bSflorian /* $OpenBSD: lsupdate.c,v 1.54 2024/08/21 15:18:00 florian Exp $ */ 2204df0f8Sclaudio 3204df0f8Sclaudio /* 4204df0f8Sclaudio * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 5367f601bSnorby * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 6204df0f8Sclaudio * 7204df0f8Sclaudio * Permission to use, copy, modify, and distribute this software for any 8204df0f8Sclaudio * purpose with or without fee is hereby granted, provided that the above 9204df0f8Sclaudio * copyright notice and this permission notice appear in all copies. 10204df0f8Sclaudio * 11204df0f8Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12204df0f8Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13204df0f8Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14204df0f8Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15204df0f8Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16204df0f8Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17204df0f8Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18204df0f8Sclaudio */ 19204df0f8Sclaudio 20204df0f8Sclaudio #include <sys/types.h> 21204df0f8Sclaudio #include <sys/socket.h> 22204df0f8Sclaudio #include <netinet/in.h> 23204df0f8Sclaudio #include <arpa/inet.h> 24204df0f8Sclaudio 25204df0f8Sclaudio #include <stdlib.h> 2653e1908aSstevesk #include <string.h> 275db4cdaaStedu #include <siphash.h> 28204df0f8Sclaudio 29204df0f8Sclaudio #include "ospf.h" 30204df0f8Sclaudio #include "ospfd.h" 31204df0f8Sclaudio #include "log.h" 32204df0f8Sclaudio #include "ospfe.h" 33204df0f8Sclaudio #include "rde.h" 34204df0f8Sclaudio 35e39620e5Snicm struct ibuf *prepare_ls_update(struct iface *); 36e39620e5Snicm int add_ls_update(struct ibuf *, struct iface *, void *, u_int16_t, 3729921c50Sclaudio u_int16_t); 38e39620e5Snicm int send_ls_update(struct ibuf *, struct iface *, struct in_addr, u_int32_t); 3932a640a4Sclaudio 4032a640a4Sclaudio void ls_retrans_list_insert(struct nbr *, struct lsa_entry *); 4132a640a4Sclaudio void ls_retrans_list_remove(struct nbr *, struct lsa_entry *); 4232a640a4Sclaudio 43204df0f8Sclaudio /* link state update packet handling */ 44204df0f8Sclaudio int 45204df0f8Sclaudio lsa_flood(struct iface *iface, struct nbr *originator, struct lsa_hdr *lsa_hdr, 4659eccb25Snorby void *data) 47204df0f8Sclaudio { 48204df0f8Sclaudio struct nbr *nbr; 49cb75af8bSclaudio struct lsa_entry *le = NULL; 50204df0f8Sclaudio int queued = 0, dont_ack = 0; 51204df0f8Sclaudio int r; 52204df0f8Sclaudio 53204df0f8Sclaudio LIST_FOREACH(nbr, &iface->nbr_list, entry) { 54204df0f8Sclaudio if (nbr == iface->self) 55204df0f8Sclaudio continue; 56204df0f8Sclaudio if (!(nbr->state & NBR_STA_FLOOD)) 57204df0f8Sclaudio continue; 58204df0f8Sclaudio 59bfaba638Sclaudio if (iface->state & IF_STA_DROTHER && !queued) 6064896ccbSclaudio while ((le = ls_retrans_list_get(iface->self, lsa_hdr))) 61bfaba638Sclaudio ls_retrans_list_free(iface->self, le); 62cb75af8bSclaudio 6364896ccbSclaudio while ((le = ls_retrans_list_get(nbr, lsa_hdr))) 64204df0f8Sclaudio ls_retrans_list_free(nbr, le); 65204df0f8Sclaudio 66204df0f8Sclaudio if (!(nbr->state & NBR_STA_FULL) && 67204df0f8Sclaudio (le = ls_req_list_get(nbr, lsa_hdr)) != NULL) { 68204df0f8Sclaudio r = lsa_newer(lsa_hdr, le->le_lsa); 69204df0f8Sclaudio if (r > 0) { 70204df0f8Sclaudio /* to flood LSA is newer than requested */ 71204df0f8Sclaudio ls_req_list_free(nbr, le); 72204df0f8Sclaudio /* new needs to be flooded */ 73204df0f8Sclaudio } else if (r < 0) { 74204df0f8Sclaudio /* to flood LSA is older than requested */ 75204df0f8Sclaudio continue; 76204df0f8Sclaudio } else { 77204df0f8Sclaudio /* LSA are equal */ 78204df0f8Sclaudio ls_req_list_free(nbr, le); 79204df0f8Sclaudio continue; 80204df0f8Sclaudio } 81204df0f8Sclaudio } 82204df0f8Sclaudio 83204df0f8Sclaudio if (nbr == originator) { 84204df0f8Sclaudio dont_ack++; 85204df0f8Sclaudio continue; 86204df0f8Sclaudio } 87204df0f8Sclaudio 881d38c082Sclaudio /* non DR or BDR router keep all lsa in one retrans list */ 89cb75af8bSclaudio if (iface->state & IF_STA_DROTHER) { 90cb75af8bSclaudio if (!queued) 9132a640a4Sclaudio ls_retrans_list_add(iface->self, data, 9232a640a4Sclaudio iface->rxmt_interval, 0); 931d38c082Sclaudio queued = 1; 9426745d1dSclaudio } else { 9532a640a4Sclaudio ls_retrans_list_add(nbr, data, iface->rxmt_interval, 0); 961d38c082Sclaudio queued = 1; 971d38c082Sclaudio } 98204df0f8Sclaudio } 99cb75af8bSclaudio 100204df0f8Sclaudio if (!queued) 101204df0f8Sclaudio return (0); 102204df0f8Sclaudio 10369d17b7aSclaudio if (iface == originator->iface && iface->self != originator) { 104204df0f8Sclaudio if (iface->dr == originator || iface->bdr == originator) 105204df0f8Sclaudio return (0); 106204df0f8Sclaudio if (iface->state & IF_STA_BACKUP) 107204df0f8Sclaudio return (0); 108204df0f8Sclaudio dont_ack++; 109204df0f8Sclaudio } 110204df0f8Sclaudio 11132a640a4Sclaudio /* 11232a640a4Sclaudio * initial flood needs to be queued separately, timeout is zero 11332a640a4Sclaudio * and oneshot has to be set because the retransimssion queues 11432a640a4Sclaudio * are already loaded. 11532a640a4Sclaudio */ 116204df0f8Sclaudio switch (iface->type) { 117204df0f8Sclaudio case IF_TYPE_POINTOPOINT: 118204df0f8Sclaudio case IF_TYPE_BROADCAST: 11932a640a4Sclaudio ls_retrans_list_add(iface->self, data, 0, 1); 120204df0f8Sclaudio break; 121204df0f8Sclaudio case IF_TYPE_NBMA: 122204df0f8Sclaudio case IF_TYPE_POINTOMULTIPOINT: 123204df0f8Sclaudio case IF_TYPE_VIRTUALLINK: 124204df0f8Sclaudio LIST_FOREACH(nbr, &iface->nbr_list, entry) { 125204df0f8Sclaudio if (nbr == iface->self) 126204df0f8Sclaudio continue; 127204df0f8Sclaudio if (!(nbr->state & NBR_STA_FLOOD)) 128204df0f8Sclaudio continue; 129204df0f8Sclaudio if (!TAILQ_EMPTY(&nbr->ls_retrans_list)) { 130204df0f8Sclaudio le = TAILQ_LAST(&nbr->ls_retrans_list, 131204df0f8Sclaudio lsa_head); 132204df0f8Sclaudio if (lsa_hdr->type != le->le_lsa->type || 133204df0f8Sclaudio lsa_hdr->ls_id != le->le_lsa->ls_id || 134204df0f8Sclaudio lsa_hdr->adv_rtr != le->le_lsa->adv_rtr) 135204df0f8Sclaudio continue; 136204df0f8Sclaudio } 13732a640a4Sclaudio ls_retrans_list_add(nbr, data, 0, 1); 138204df0f8Sclaudio } 139204df0f8Sclaudio break; 140204df0f8Sclaudio default: 141204df0f8Sclaudio fatalx("lsa_flood: unknown interface type"); 142204df0f8Sclaudio } 143204df0f8Sclaudio 144204df0f8Sclaudio return (dont_ack == 2); 145204df0f8Sclaudio } 146204df0f8Sclaudio 147e39620e5Snicm struct ibuf * 14832a640a4Sclaudio prepare_ls_update(struct iface *iface) 149204df0f8Sclaudio { 150e39620e5Snicm struct ibuf *buf; 151204df0f8Sclaudio 1529c09f078Sclaudio if ((buf = ibuf_dynamic(iface->mtu - sizeof(struct ip), 1539c09f078Sclaudio IP_MAXPACKET - sizeof(struct ip))) == NULL) 15498c612a2Snorby fatal("prepare_ls_update"); 155204df0f8Sclaudio 156204df0f8Sclaudio /* OSPF header */ 1570491ce75Sclaudio if (gen_ospf_hdr(buf, iface, PACKET_TYPE_LS_UPDATE)) 1580491ce75Sclaudio goto fail; 159204df0f8Sclaudio 16032a640a4Sclaudio /* reserve space for number of lsa field */ 16143e70bb4Sclaudio if (ibuf_add_zero(buf, sizeof(u_int32_t)) == -1) 1620491ce75Sclaudio goto fail; 163204df0f8Sclaudio 16432a640a4Sclaudio return (buf); 16532a640a4Sclaudio fail: 16632a640a4Sclaudio log_warn("prepare_ls_update"); 167e39620e5Snicm ibuf_free(buf); 16832a640a4Sclaudio return (NULL); 16932a640a4Sclaudio } 17032a640a4Sclaudio 17132a640a4Sclaudio int 172e39620e5Snicm add_ls_update(struct ibuf *buf, struct iface *iface, void *data, u_int16_t len, 17364896ccbSclaudio u_int16_t older) 17432a640a4Sclaudio { 17507669e8bSclaudio size_t ageoff; 17632a640a4Sclaudio u_int16_t age; 17732a640a4Sclaudio 1789c09f078Sclaudio if ((size_t)iface->mtu < sizeof(struct ip) + sizeof(struct ospf_hdr) + 1799c09f078Sclaudio sizeof(u_int32_t) + ibuf_size(buf) + len + MD5_DIGEST_LENGTH) { 1809c09f078Sclaudio /* start new packet unless this is the first LSA to pack */ 1819c09f078Sclaudio if (ibuf_size(buf) > sizeof(struct ospf_hdr) + 1829c09f078Sclaudio sizeof(u_int32_t)) 18332a640a4Sclaudio return (0); 1849c09f078Sclaudio } 18532a640a4Sclaudio 18607669e8bSclaudio ageoff = ibuf_size(buf); 187e39620e5Snicm if (ibuf_add(buf, data, len)) { 18832a640a4Sclaudio log_warn("add_ls_update"); 18932a640a4Sclaudio return (0); 19032a640a4Sclaudio } 191204df0f8Sclaudio 192611e3786Sclaudio /* age LSA before sending it out */ 1930491ce75Sclaudio memcpy(&age, data, sizeof(age)); 194204df0f8Sclaudio age = ntohs(age); 19564896ccbSclaudio if ((age += older + iface->transmit_delay) >= MAX_AGE) 196cd3874a6Sclaudio age = MAX_AGE; 19743e70bb4Sclaudio if (ibuf_set_n16(buf, ageoff, age) == -1) { 19843e70bb4Sclaudio log_warn("add_ls_update"); 19943e70bb4Sclaudio return (0); 20043e70bb4Sclaudio } 201204df0f8Sclaudio 20232a640a4Sclaudio return (1); 20332a640a4Sclaudio } 20432a640a4Sclaudio 20532a640a4Sclaudio int 206e39620e5Snicm send_ls_update(struct ibuf *buf, struct iface *iface, struct in_addr addr, 20732a640a4Sclaudio u_int32_t nlsa) 20832a640a4Sclaudio { 20932a640a4Sclaudio struct sockaddr_in dst; 21032a640a4Sclaudio 21143e70bb4Sclaudio if (ibuf_set_n32(buf, sizeof(struct ospf_hdr), nlsa) == -1) 21243e70bb4Sclaudio goto fail; 213204df0f8Sclaudio /* update authentication and calculate checksum */ 2140491ce75Sclaudio if (auth_gen(buf, iface)) 2150491ce75Sclaudio goto fail; 216204df0f8Sclaudio 21732a640a4Sclaudio /* set destination */ 21832a640a4Sclaudio dst.sin_family = AF_INET; 21932a640a4Sclaudio dst.sin_len = sizeof(struct sockaddr_in); 22032a640a4Sclaudio dst.sin_addr.s_addr = addr.s_addr; 22132a640a4Sclaudio 22225a742ccSremi if (send_packet(iface, buf, &dst) == -1) 22325a742ccSremi goto fail; 224204df0f8Sclaudio 225e39620e5Snicm ibuf_free(buf); 22625a742ccSremi return (0); 2270491ce75Sclaudio fail: 22825a742ccSremi log_warn("%s", __func__); 229e39620e5Snicm ibuf_free(buf); 2300491ce75Sclaudio return (-1); 231204df0f8Sclaudio } 232204df0f8Sclaudio 233204df0f8Sclaudio void 234204df0f8Sclaudio recv_ls_update(struct nbr *nbr, char *buf, u_int16_t len) 235204df0f8Sclaudio { 236204df0f8Sclaudio struct lsa_hdr lsa; 237204df0f8Sclaudio u_int32_t nlsa; 238204df0f8Sclaudio 239204df0f8Sclaudio if (len < sizeof(nlsa)) { 2409cc19ef1Ssthen log_warnx("recv_ls_update: bad packet size, " 2419cc19ef1Ssthen "neighbor ID %s (%s)", inet_ntoa(nbr->id), 2429cc19ef1Ssthen nbr->iface->name); 243204df0f8Sclaudio return; 244204df0f8Sclaudio } 245204df0f8Sclaudio memcpy(&nlsa, buf, sizeof(nlsa)); 246204df0f8Sclaudio nlsa = ntohl(nlsa); 247204df0f8Sclaudio buf += sizeof(nlsa); 248204df0f8Sclaudio len -= sizeof(nlsa); 249204df0f8Sclaudio 250204df0f8Sclaudio switch (nbr->state) { 251204df0f8Sclaudio case NBR_STA_DOWN: 252204df0f8Sclaudio case NBR_STA_ATTEMPT: 253204df0f8Sclaudio case NBR_STA_INIT: 254204df0f8Sclaudio case NBR_STA_2_WAY: 255204df0f8Sclaudio case NBR_STA_XSTRT: 256204df0f8Sclaudio case NBR_STA_SNAP: 257204df0f8Sclaudio log_debug("recv_ls_update: packet ignored in state %s, " 2589cc19ef1Ssthen "neighbor ID %s (%s)", nbr_state_name(nbr->state), 2599cc19ef1Ssthen inet_ntoa(nbr->id), nbr->iface->name); 260204df0f8Sclaudio break; 261204df0f8Sclaudio case NBR_STA_XCHNG: 262204df0f8Sclaudio case NBR_STA_LOAD: 263204df0f8Sclaudio case NBR_STA_FULL: 264204df0f8Sclaudio for (; nlsa > 0 && len > 0; nlsa--) { 265204df0f8Sclaudio if (len < sizeof(lsa)) { 266204df0f8Sclaudio log_warnx("recv_ls_update: bad packet size, " 2679cc19ef1Ssthen "neighbor ID %s (%s)", inet_ntoa(nbr->id), 2689cc19ef1Ssthen nbr->iface->name); 269204df0f8Sclaudio return; 270204df0f8Sclaudio } 271204df0f8Sclaudio memcpy(&lsa, buf, sizeof(lsa)); 272204df0f8Sclaudio if (len < ntohs(lsa.len)) { 273204df0f8Sclaudio log_warnx("recv_ls_update: bad packet size, " 2749cc19ef1Ssthen "neighbor ID %s (%s)", inet_ntoa(nbr->id), 2759cc19ef1Ssthen nbr->iface->name); 276204df0f8Sclaudio return; 277204df0f8Sclaudio } 2786efdfed8Sclaudio ospfe_imsg_compose_rde(IMSG_LS_UPD, nbr->peerid, 0, 2796efdfed8Sclaudio buf, ntohs(lsa.len)); 280204df0f8Sclaudio buf += ntohs(lsa.len); 281204df0f8Sclaudio len -= ntohs(lsa.len); 282204df0f8Sclaudio } 283204df0f8Sclaudio if (nlsa > 0 || len > 0) { 284204df0f8Sclaudio log_warnx("recv_ls_update: bad packet size, " 2859cc19ef1Ssthen "neighbor ID %s (%s)", inet_ntoa(nbr->id), 2869cc19ef1Ssthen nbr->iface->name); 287204df0f8Sclaudio return; 288204df0f8Sclaudio } 289204df0f8Sclaudio break; 290204df0f8Sclaudio default: 291204df0f8Sclaudio fatalx("recv_ls_update: unknown neighbor state"); 292204df0f8Sclaudio } 293204df0f8Sclaudio } 294204df0f8Sclaudio 295204df0f8Sclaudio /* link state retransmit list */ 296cb75af8bSclaudio void 29732a640a4Sclaudio ls_retrans_list_add(struct nbr *nbr, struct lsa_hdr *lsa, 29832a640a4Sclaudio unsigned short timeout, unsigned short oneshot) 299204df0f8Sclaudio { 300cb75af8bSclaudio struct timeval tv; 301204df0f8Sclaudio struct lsa_entry *le; 302204df0f8Sclaudio struct lsa_ref *ref; 303204df0f8Sclaudio 304cb75af8bSclaudio if ((ref = lsa_cache_get(lsa)) == NULL) 305cb75af8bSclaudio fatalx("King Bula sez: somebody forgot to lsa_cache_add"); 306204df0f8Sclaudio 307204df0f8Sclaudio if ((le = calloc(1, sizeof(*le))) == NULL) 308204df0f8Sclaudio fatal("ls_retrans_list_add"); 309204df0f8Sclaudio 310204df0f8Sclaudio le->le_ref = ref; 31132a640a4Sclaudio le->le_when = timeout; 31232a640a4Sclaudio le->le_oneshot = oneshot; 31332a640a4Sclaudio 31432a640a4Sclaudio ls_retrans_list_insert(nbr, le); 315204df0f8Sclaudio 316cb75af8bSclaudio if (!evtimer_pending(&nbr->ls_retrans_timer, NULL)) { 317cb75af8bSclaudio timerclear(&tv); 31832a640a4Sclaudio tv.tv_sec = TAILQ_FIRST(&nbr->ls_retrans_list)->le_when; 319cb75af8bSclaudio 320cb75af8bSclaudio if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1) 3213aede346Sclaudio fatal("ls_retrans_list_add"); 322cb75af8bSclaudio } 323204df0f8Sclaudio } 324204df0f8Sclaudio 325204df0f8Sclaudio int 326204df0f8Sclaudio ls_retrans_list_del(struct nbr *nbr, struct lsa_hdr *lsa_hdr) 327204df0f8Sclaudio { 328204df0f8Sclaudio struct lsa_entry *le; 329204df0f8Sclaudio 33064896ccbSclaudio if ((le = ls_retrans_list_get(nbr, lsa_hdr)) == NULL) 331cb75af8bSclaudio return (-1); 3328e73b4e2Sbluhm /* 3338e73b4e2Sbluhm * Compare LSA with the Ack by comparing not only the seq_num and 3348e73b4e2Sbluhm * checksum but also the age field. Since we only care about MAX_AGE 3358e73b4e2Sbluhm * vs. non-MAX_AGE LSA, a simple >= comparison is good enough. This 3368e73b4e2Sbluhm * ensures that a LSA withdrawal is not acked by a previous update. 3378e73b4e2Sbluhm */ 338204df0f8Sclaudio if (lsa_hdr->seq_num == le->le_ref->hdr.seq_num && 3398e73b4e2Sbluhm lsa_hdr->ls_chksum == le->le_ref->hdr.ls_chksum && 3408e73b4e2Sbluhm ntohs(lsa_hdr->age) >= ntohs(le->le_ref->hdr.age)) { 341204df0f8Sclaudio ls_retrans_list_free(nbr, le); 342204df0f8Sclaudio return (0); 343204df0f8Sclaudio } 344204df0f8Sclaudio 345cb75af8bSclaudio return (-1); 346204df0f8Sclaudio } 347204df0f8Sclaudio 348204df0f8Sclaudio struct lsa_entry * 349204df0f8Sclaudio ls_retrans_list_get(struct nbr *nbr, struct lsa_hdr *lsa_hdr) 350204df0f8Sclaudio { 351204df0f8Sclaudio struct lsa_entry *le; 352204df0f8Sclaudio 353204df0f8Sclaudio TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry) { 354204df0f8Sclaudio if ((lsa_hdr->type == le->le_ref->hdr.type) && 355204df0f8Sclaudio (lsa_hdr->ls_id == le->le_ref->hdr.ls_id) && 356204df0f8Sclaudio (lsa_hdr->adv_rtr == le->le_ref->hdr.adv_rtr)) 357204df0f8Sclaudio return (le); 358204df0f8Sclaudio } 359204df0f8Sclaudio return (NULL); 360204df0f8Sclaudio } 361204df0f8Sclaudio 362204df0f8Sclaudio void 36332a640a4Sclaudio ls_retrans_list_insert(struct nbr *nbr, struct lsa_entry *new) 36432a640a4Sclaudio { 36532a640a4Sclaudio struct lsa_entry *le; 36632a640a4Sclaudio unsigned short when = new->le_when; 36732a640a4Sclaudio 36832a640a4Sclaudio TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry) { 36932a640a4Sclaudio if (when < le->le_when) { 37032a640a4Sclaudio new->le_when = when; 37132a640a4Sclaudio TAILQ_INSERT_BEFORE(le, new, entry); 3722b5aa3c5Sclaudio nbr->ls_ret_cnt++; 37332a640a4Sclaudio return; 37432a640a4Sclaudio } 37532a640a4Sclaudio when -= le->le_when; 37632a640a4Sclaudio } 37732a640a4Sclaudio new->le_when = when; 37832a640a4Sclaudio TAILQ_INSERT_TAIL(&nbr->ls_retrans_list, new, entry); 3791ba81fefSnorby nbr->ls_ret_cnt++; 38032a640a4Sclaudio } 38132a640a4Sclaudio 38232a640a4Sclaudio void 38332a640a4Sclaudio ls_retrans_list_remove(struct nbr *nbr, struct lsa_entry *le) 38432a640a4Sclaudio { 38532a640a4Sclaudio struct timeval tv; 38632a640a4Sclaudio struct lsa_entry *next = TAILQ_NEXT(le, entry); 38732a640a4Sclaudio int reset = 0; 38832a640a4Sclaudio 38932a640a4Sclaudio /* adjust timeout of next entry */ 39032a640a4Sclaudio if (next) 39132a640a4Sclaudio next->le_when += le->le_when; 39232a640a4Sclaudio 39332a640a4Sclaudio if (TAILQ_FIRST(&nbr->ls_retrans_list) == le && 39432a640a4Sclaudio evtimer_pending(&nbr->ls_retrans_timer, NULL)) 39532a640a4Sclaudio reset = 1; 39632a640a4Sclaudio 39732a640a4Sclaudio TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry); 3981ba81fefSnorby nbr->ls_ret_cnt--; 39932a640a4Sclaudio 40032a640a4Sclaudio if (reset && TAILQ_FIRST(&nbr->ls_retrans_list)) { 4013aede346Sclaudio if (evtimer_del(&nbr->ls_retrans_timer) == -1) 4023aede346Sclaudio fatal("ls_retrans_list_remove"); 40332a640a4Sclaudio 40432a640a4Sclaudio timerclear(&tv); 40532a640a4Sclaudio tv.tv_sec = TAILQ_FIRST(&nbr->ls_retrans_list)->le_when; 40632a640a4Sclaudio 40732a640a4Sclaudio if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1) 4083aede346Sclaudio fatal("ls_retrans_list_remove"); 40932a640a4Sclaudio } 41032a640a4Sclaudio } 41132a640a4Sclaudio 41232a640a4Sclaudio void 413204df0f8Sclaudio ls_retrans_list_free(struct nbr *nbr, struct lsa_entry *le) 414204df0f8Sclaudio { 41532a640a4Sclaudio ls_retrans_list_remove(nbr, le); 416204df0f8Sclaudio 417204df0f8Sclaudio lsa_cache_put(le->le_ref, nbr); 418204df0f8Sclaudio free(le); 419204df0f8Sclaudio } 420204df0f8Sclaudio 421204df0f8Sclaudio void 422204df0f8Sclaudio ls_retrans_list_clr(struct nbr *nbr) 423204df0f8Sclaudio { 424204df0f8Sclaudio struct lsa_entry *le; 425204df0f8Sclaudio 426204df0f8Sclaudio while ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL) 427204df0f8Sclaudio ls_retrans_list_free(nbr, le); 4281ba81fefSnorby 4291ba81fefSnorby nbr->ls_ret_cnt = 0; 430204df0f8Sclaudio } 431204df0f8Sclaudio 432cb75af8bSclaudio void 433cb75af8bSclaudio ls_retrans_timer(int fd, short event, void *bula) 434cb75af8bSclaudio { 435cb75af8bSclaudio struct timeval tv; 43664896ccbSclaudio struct timespec tp; 437cb75af8bSclaudio struct in_addr addr; 438cb75af8bSclaudio struct nbr *nbr = bula; 439cb75af8bSclaudio struct lsa_entry *le; 440e39620e5Snicm struct ibuf *buf; 44164896ccbSclaudio time_t now; 44264896ccbSclaudio int d; 44332a640a4Sclaudio u_int32_t nlsa = 0; 44432a640a4Sclaudio 44532a640a4Sclaudio if ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL) 44632a640a4Sclaudio le->le_when = 0; /* timer fired */ 44732a640a4Sclaudio else 44832a640a4Sclaudio return; /* queue empty, nothing to do */ 449cb75af8bSclaudio 45064896ccbSclaudio clock_gettime(CLOCK_MONOTONIC, &tp); 45164896ccbSclaudio now = tp.tv_sec; 45264896ccbSclaudio 453cb75af8bSclaudio if (nbr->iface->self == nbr) { 454cb75af8bSclaudio /* 45532a640a4Sclaudio * oneshot needs to be set for lsa queued for flooding, 45632a640a4Sclaudio * if oneshot is not set then the lsa needs to be converted 45732a640a4Sclaudio * because the router switched lately to DR or BDR 458cb75af8bSclaudio */ 45932a640a4Sclaudio if (le->le_oneshot && nbr->iface->state & IF_STA_DRORBDR) 460*4f4fe40bSflorian inet_pton(AF_INET, AllSPFRouters, &addr); 46132a640a4Sclaudio else if (nbr->iface->state & IF_STA_DRORBDR) { 46232a640a4Sclaudio /* 46332a640a4Sclaudio * old retransmission needs to be converted into 46432a640a4Sclaudio * flood by rerunning the lsa_flood. 46532a640a4Sclaudio */ 466cb75af8bSclaudio lsa_flood(nbr->iface, nbr, &le->le_ref->hdr, 46759eccb25Snorby le->le_ref->data); 468cb75af8bSclaudio ls_retrans_list_free(nbr, le); 46932a640a4Sclaudio /* ls_retrans_list_free retriggers the timer */ 470cb75af8bSclaudio return; 47150cd5907Smarkus } else if (nbr->iface->type == IF_TYPE_POINTOPOINT) 472ef209401Sremi memcpy(&addr, &nbr->addr, sizeof(addr)); 47350cd5907Smarkus else 474*4f4fe40bSflorian inet_pton(AF_INET, AllDRouters, &addr); 475cb75af8bSclaudio } else 476cb75af8bSclaudio memcpy(&addr, &nbr->addr, sizeof(addr)); 477cb75af8bSclaudio 47832a640a4Sclaudio if ((buf = prepare_ls_update(nbr->iface)) == NULL) { 47932a640a4Sclaudio le->le_when = 1; 48032a640a4Sclaudio goto done; 48132a640a4Sclaudio } 482cb75af8bSclaudio 48332a640a4Sclaudio while ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL && 48432a640a4Sclaudio le->le_when == 0) { 48564896ccbSclaudio d = now - le->le_ref->stamp; 48664896ccbSclaudio if (d < 0) 48764896ccbSclaudio d = 0; 48864896ccbSclaudio else if (d > MAX_AGE) 48964896ccbSclaudio d = MAX_AGE; 49064896ccbSclaudio 49132a640a4Sclaudio if (add_ls_update(buf, nbr->iface, le->le_ref->data, 4929c09f078Sclaudio le->le_ref->len, d) == 0) { 4939c09f078Sclaudio if (nlsa == 0) { 49457ee193cSjca /* something bad happened retry later */ 4959c09f078Sclaudio log_warnx("ls_retrans_timer: sending LS update " 4969cc19ef1Ssthen "to neighbor ID %s (%s) failed", 4979cc19ef1Ssthen inet_ntoa(nbr->id), nbr->iface->name); 4989c09f078Sclaudio TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry); 4999c09f078Sclaudio nbr->ls_ret_cnt--; 5009c09f078Sclaudio le->le_when = nbr->iface->rxmt_interval; 5019c09f078Sclaudio ls_retrans_list_insert(nbr, le); 5029c09f078Sclaudio } 50332a640a4Sclaudio break; 5049c09f078Sclaudio } 50532a640a4Sclaudio nlsa++; 50632a640a4Sclaudio if (le->le_oneshot) 50732a640a4Sclaudio ls_retrans_list_free(nbr, le); 50832a640a4Sclaudio else { 50932a640a4Sclaudio TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry); 5102b5aa3c5Sclaudio nbr->ls_ret_cnt--; 51132a640a4Sclaudio le->le_when = nbr->iface->rxmt_interval; 51232a640a4Sclaudio ls_retrans_list_insert(nbr, le); 51332a640a4Sclaudio } 51432a640a4Sclaudio } 5159c09f078Sclaudio if (nlsa) 51632a640a4Sclaudio send_ls_update(buf, nbr->iface, addr, nlsa); 5179c09f078Sclaudio else 5189c09f078Sclaudio ibuf_free(buf); 51932a640a4Sclaudio 52032a640a4Sclaudio done: 521cb75af8bSclaudio if ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL) { 522cb75af8bSclaudio timerclear(&tv); 52332a640a4Sclaudio tv.tv_sec = le->le_when; 524cb75af8bSclaudio 525cb75af8bSclaudio if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1) 5263aede346Sclaudio fatal("ls_retrans_timer"); 527cb75af8bSclaudio } 528cb75af8bSclaudio } 529cb75af8bSclaudio 530204df0f8Sclaudio LIST_HEAD(lsa_cache_head, lsa_ref); 531204df0f8Sclaudio 532204df0f8Sclaudio struct lsa_cache { 533204df0f8Sclaudio struct lsa_cache_head *hashtbl; 534204df0f8Sclaudio u_int32_t hashmask; 535204df0f8Sclaudio } lsacache; 536204df0f8Sclaudio 5375db4cdaaStedu SIPHASH_KEY lsacachekey; 5385db4cdaaStedu 539204df0f8Sclaudio struct lsa_ref *lsa_cache_look(struct lsa_hdr *); 540204df0f8Sclaudio 541204df0f8Sclaudio void 542204df0f8Sclaudio lsa_cache_init(u_int32_t hashsize) 543204df0f8Sclaudio { 544204df0f8Sclaudio u_int32_t hs, i; 545204df0f8Sclaudio 546204df0f8Sclaudio for (hs = 1; hs < hashsize; hs <<= 1) 547204df0f8Sclaudio ; 548204df0f8Sclaudio lsacache.hashtbl = calloc(hs, sizeof(struct lsa_cache_head)); 549204df0f8Sclaudio if (lsacache.hashtbl == NULL) 550204df0f8Sclaudio fatal("lsa_cache_init"); 551204df0f8Sclaudio 552204df0f8Sclaudio for (i = 0; i < hs; i++) 553204df0f8Sclaudio LIST_INIT(&lsacache.hashtbl[i]); 5545db4cdaaStedu arc4random_buf(&lsacachekey, sizeof(lsacachekey)); 555204df0f8Sclaudio 556204df0f8Sclaudio lsacache.hashmask = hs - 1; 557204df0f8Sclaudio } 558204df0f8Sclaudio 559d4b7cca4Stedu static uint32_t 5605db4cdaaStedu lsa_hash_hdr(const struct lsa_hdr *hdr) 5615db4cdaaStedu { 5625db4cdaaStedu return SipHash24(&lsacachekey, hdr, sizeof(*hdr)); 5635db4cdaaStedu } 5645db4cdaaStedu 565204df0f8Sclaudio struct lsa_ref * 566204df0f8Sclaudio lsa_cache_add(void *data, u_int16_t len) 567204df0f8Sclaudio { 568204df0f8Sclaudio struct lsa_cache_head *head; 569204df0f8Sclaudio struct lsa_ref *ref, *old; 570f896e954Snorby struct timespec tp; 571204df0f8Sclaudio 572204df0f8Sclaudio if ((ref = calloc(1, sizeof(*ref))) == NULL) 573204df0f8Sclaudio fatal("lsa_cache_add"); 574204df0f8Sclaudio memcpy(&ref->hdr, data, sizeof(ref->hdr)); 575204df0f8Sclaudio 576204df0f8Sclaudio if ((old = lsa_cache_look(&ref->hdr))) { 577204df0f8Sclaudio free(ref); 578204df0f8Sclaudio old->refcnt++; 579204df0f8Sclaudio return (old); 580204df0f8Sclaudio } 581204df0f8Sclaudio 582204df0f8Sclaudio if ((ref->data = malloc(len)) == NULL) 583204df0f8Sclaudio fatal("lsa_cache_add"); 584204df0f8Sclaudio memcpy(ref->data, data, len); 585f896e954Snorby 586f896e954Snorby clock_gettime(CLOCK_MONOTONIC, &tp); 587f896e954Snorby ref->stamp = tp.tv_sec; 588204df0f8Sclaudio ref->len = len; 589204df0f8Sclaudio ref->refcnt = 1; 590204df0f8Sclaudio 5915db4cdaaStedu head = &lsacache.hashtbl[lsa_hash_hdr(&ref->hdr) & lsacache.hashmask]; 592204df0f8Sclaudio LIST_INSERT_HEAD(head, ref, entry); 593204df0f8Sclaudio return (ref); 594204df0f8Sclaudio } 595204df0f8Sclaudio 596204df0f8Sclaudio struct lsa_ref * 597204df0f8Sclaudio lsa_cache_get(struct lsa_hdr *lsa_hdr) 598204df0f8Sclaudio { 599204df0f8Sclaudio struct lsa_ref *ref; 600204df0f8Sclaudio 601204df0f8Sclaudio ref = lsa_cache_look(lsa_hdr); 602204df0f8Sclaudio if (ref) 603204df0f8Sclaudio ref->refcnt++; 604204df0f8Sclaudio 605204df0f8Sclaudio return (ref); 606204df0f8Sclaudio } 607204df0f8Sclaudio 608204df0f8Sclaudio void 609204df0f8Sclaudio lsa_cache_put(struct lsa_ref *ref, struct nbr *nbr) 610204df0f8Sclaudio { 611204df0f8Sclaudio if (--ref->refcnt > 0) 612204df0f8Sclaudio return; 613204df0f8Sclaudio 614204df0f8Sclaudio if (ntohs(ref->hdr.age) >= MAX_AGE) 615204df0f8Sclaudio ospfe_imsg_compose_rde(IMSG_LS_MAXAGE, nbr->peerid, 0, 616204df0f8Sclaudio ref->data, sizeof(struct lsa_hdr)); 617204df0f8Sclaudio 618204df0f8Sclaudio free(ref->data); 619204df0f8Sclaudio LIST_REMOVE(ref, entry); 620204df0f8Sclaudio free(ref); 621204df0f8Sclaudio } 622204df0f8Sclaudio 623204df0f8Sclaudio struct lsa_ref * 624204df0f8Sclaudio lsa_cache_look(struct lsa_hdr *lsa_hdr) 625204df0f8Sclaudio { 626204df0f8Sclaudio struct lsa_cache_head *head; 627204df0f8Sclaudio struct lsa_ref *ref; 628204df0f8Sclaudio 6295db4cdaaStedu head = &lsacache.hashtbl[lsa_hash_hdr(lsa_hdr) & lsacache.hashmask]; 63064896ccbSclaudio 631204df0f8Sclaudio LIST_FOREACH(ref, head, entry) { 63264896ccbSclaudio if (memcmp(&ref->hdr, lsa_hdr, sizeof(*lsa_hdr)) == 0) 633204df0f8Sclaudio /* found match */ 634204df0f8Sclaudio return (ref); 635204df0f8Sclaudio } 63664896ccbSclaudio 637204df0f8Sclaudio return (NULL); 638204df0f8Sclaudio } 639