1*4f4fe40bSflorian /* $OpenBSD: hello.c,v 1.27 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 #include <sys/time.h> 25204df0f8Sclaudio #include <stdlib.h> 26204df0f8Sclaudio #include <string.h> 27204df0f8Sclaudio #include <event.h> 28204df0f8Sclaudio 29204df0f8Sclaudio #include "ospfd.h" 30204df0f8Sclaudio #include "ospf.h" 31204df0f8Sclaudio #include "log.h" 32204df0f8Sclaudio #include "ospfe.h" 33204df0f8Sclaudio 34204df0f8Sclaudio extern struct ospfd_conf *oeconf; 35204df0f8Sclaudio 36204df0f8Sclaudio /* hello packet handling */ 37204df0f8Sclaudio int 38204df0f8Sclaudio send_hello(struct iface *iface) 39204df0f8Sclaudio { 40204df0f8Sclaudio struct sockaddr_in dst; 410491ce75Sclaudio struct hello_hdr hello; 42204df0f8Sclaudio struct nbr *nbr; 43e39620e5Snicm struct ibuf *buf; 44204df0f8Sclaudio 45204df0f8Sclaudio dst.sin_family = AF_INET; 46204df0f8Sclaudio dst.sin_len = sizeof(struct sockaddr_in); 47204df0f8Sclaudio 48204df0f8Sclaudio switch (iface->type) { 49204df0f8Sclaudio case IF_TYPE_POINTOPOINT: 50204df0f8Sclaudio case IF_TYPE_BROADCAST: 51*4f4fe40bSflorian inet_pton(AF_INET, AllSPFRouters, &dst.sin_addr); 52204df0f8Sclaudio break; 53204df0f8Sclaudio case IF_TYPE_NBMA: 54204df0f8Sclaudio case IF_TYPE_POINTOMULTIPOINT: 550b593adcSclaudio log_debug("send_hello: type %s not supported, interface %s", 560b593adcSclaudio if_type_name(iface->type), iface->name); 570b593adcSclaudio return (-1); 58204df0f8Sclaudio case IF_TYPE_VIRTUALLINK: 59ebc9e90aSnorby dst.sin_addr = iface->dst; 60204df0f8Sclaudio break; 61204df0f8Sclaudio default: 62204df0f8Sclaudio fatalx("send_hello: unknown interface type"); 63204df0f8Sclaudio } 64204df0f8Sclaudio 656860b38aSclaudio if ((buf = ibuf_dynamic(PKG_DEF_SIZE, 666860b38aSclaudio IP_MAXPACKET - sizeof(struct ip))) == NULL) 670b593adcSclaudio fatal("send_hello"); 680b593adcSclaudio 69204df0f8Sclaudio /* OSPF header */ 700491ce75Sclaudio if (gen_ospf_hdr(buf, iface, PACKET_TYPE_HELLO)) 710491ce75Sclaudio goto fail; 72204df0f8Sclaudio 73204df0f8Sclaudio /* hello header */ 740491ce75Sclaudio hello.mask = iface->mask.s_addr; 750491ce75Sclaudio hello.hello_interval = htons(iface->hello_interval); 763ffd5c19Sclaudio hello.opts = area_ospf_options(iface->area); 770491ce75Sclaudio hello.rtr_priority = iface->priority; 780491ce75Sclaudio hello.rtr_dead_interval = htonl(iface->dead_interval); 79204df0f8Sclaudio 80ed5b76e9Sclaudio if (iface->dr) { 810491ce75Sclaudio hello.d_rtr = iface->dr->addr.s_addr; 82ed5b76e9Sclaudio iface->self->dr.s_addr = iface->dr->addr.s_addr; 830491ce75Sclaudio } else 840491ce75Sclaudio hello.d_rtr = 0; 85ed5b76e9Sclaudio if (iface->bdr) { 860491ce75Sclaudio hello.bd_rtr = iface->bdr->addr.s_addr; 87ed5b76e9Sclaudio iface->self->bdr.s_addr = iface->bdr->addr.s_addr; 880491ce75Sclaudio } else 890491ce75Sclaudio hello.bd_rtr = 0; 900491ce75Sclaudio 91e39620e5Snicm if (ibuf_add(buf, &hello, sizeof(hello))) 920491ce75Sclaudio goto fail; 93204df0f8Sclaudio 94204df0f8Sclaudio /* active neighbor(s) */ 95204df0f8Sclaudio LIST_FOREACH(nbr, &iface->nbr_list, entry) { 960491ce75Sclaudio if ((nbr->state >= NBR_STA_INIT) && (nbr != iface->self)) 97e39620e5Snicm if (ibuf_add(buf, &nbr->id, sizeof(nbr->id))) 980491ce75Sclaudio goto fail; 99204df0f8Sclaudio } 100204df0f8Sclaudio 101204df0f8Sclaudio /* update authentication and calculate checksum */ 1020491ce75Sclaudio if (auth_gen(buf, iface)) 1030491ce75Sclaudio goto fail; 104204df0f8Sclaudio 10525a742ccSremi if (send_packet(iface, buf, &dst) == -1) 10625a742ccSremi goto fail; 10725a742ccSremi 108e39620e5Snicm ibuf_free(buf); 10925a742ccSremi return (0); 1100491ce75Sclaudio fail: 11125a742ccSremi log_warn("%s", __func__); 112e39620e5Snicm ibuf_free(buf); 1130491ce75Sclaudio return (-1); 114204df0f8Sclaudio } 115204df0f8Sclaudio 116204df0f8Sclaudio void 117204df0f8Sclaudio recv_hello(struct iface *iface, struct in_addr src, u_int32_t rtr_id, char *buf, 118204df0f8Sclaudio u_int16_t len) 119204df0f8Sclaudio { 120204df0f8Sclaudio struct hello_hdr hello; 121e9496ee4Sclaudio struct nbr *nbr = NULL, *dr; 122204df0f8Sclaudio u_int32_t nbr_id; 123e9496ee4Sclaudio int nbr_change = 0; 124204df0f8Sclaudio 12565b16aecSjacekm if (len < sizeof(hello) || (len & 0x03)) { 126204df0f8Sclaudio log_warnx("recv_hello: bad packet size, interface %s", 127204df0f8Sclaudio iface->name); 128204df0f8Sclaudio return; 129204df0f8Sclaudio } 130204df0f8Sclaudio 131204df0f8Sclaudio memcpy(&hello, buf, sizeof(hello)); 132204df0f8Sclaudio buf += sizeof(hello); 133204df0f8Sclaudio len -= sizeof(hello); 134204df0f8Sclaudio 135204df0f8Sclaudio if (iface->type != IF_TYPE_POINTOPOINT && 136204df0f8Sclaudio iface->type != IF_TYPE_VIRTUALLINK) 137204df0f8Sclaudio if (hello.mask != iface->mask.s_addr) { 138204df0f8Sclaudio log_warnx("recv_hello: invalid netmask, interface %s", 139204df0f8Sclaudio iface->name); 140204df0f8Sclaudio return; 141204df0f8Sclaudio } 142204df0f8Sclaudio 143204df0f8Sclaudio if (ntohs(hello.hello_interval) != iface->hello_interval) { 144204df0f8Sclaudio log_warnx("recv_hello: invalid hello-interval %d, " 145204df0f8Sclaudio "interface %s", ntohs(hello.hello_interval), 146204df0f8Sclaudio iface->name); 147204df0f8Sclaudio return; 148204df0f8Sclaudio } 149204df0f8Sclaudio 150204df0f8Sclaudio if (ntohl(hello.rtr_dead_interval) != iface->dead_interval) { 151204df0f8Sclaudio log_warnx("recv_hello: invalid router-dead-interval %d, " 152204df0f8Sclaudio "interface %s", ntohl(hello.rtr_dead_interval), 153204df0f8Sclaudio iface->name); 154204df0f8Sclaudio return; 155204df0f8Sclaudio } 156204df0f8Sclaudio 157204df0f8Sclaudio if ((hello.opts & OSPF_OPTION_E && iface->area->stub) || 158204df0f8Sclaudio ((hello.opts & OSPF_OPTION_E) == 0 && !iface->area->stub)) { 159204df0f8Sclaudio log_warnx("recv_hello: ExternalRoutingCapability mismatch, " 160204df0f8Sclaudio "interface %s", iface->name); 161204df0f8Sclaudio return; 162204df0f8Sclaudio } 163204df0f8Sclaudio 164aaa979c9Sclaudio /* 165aaa979c9Sclaudio * Match router-id, in case of conflict moan and ignore hello. 166aaa979c9Sclaudio * Only the router-id is compared since the source IP on NBMA, 167aaa979c9Sclaudio * broadcast and point-to-multipoint interfaces was already 168aaa979c9Sclaudio * compared in find_iface() and only IPs in the same subnet 1693a50f0a9Sjmc * are accepted. This is not exactly what the RFC specifies 170aaa979c9Sclaudio * but works far better. 171aaa979c9Sclaudio */ 172204df0f8Sclaudio LIST_FOREACH(nbr, &iface->nbr_list, entry) { 173aaa979c9Sclaudio if (nbr == iface->self) { 174aaa979c9Sclaudio if (nbr->id.s_addr == rtr_id) { 1757cfdfd15Ssthen log_warnx("recv_hello: Router-ID collision on " 176aaa979c9Sclaudio "interface %s neighbor IP %s", iface->name, 177aaa979c9Sclaudio inet_ntoa(src)); 178aaa979c9Sclaudio return; 179aaa979c9Sclaudio } 180204df0f8Sclaudio continue; 181aaa979c9Sclaudio } 182204df0f8Sclaudio if (nbr->id.s_addr == rtr_id) 183204df0f8Sclaudio break; 184204df0f8Sclaudio } 185204df0f8Sclaudio 186e9496ee4Sclaudio if (!nbr) { 187204df0f8Sclaudio nbr = nbr_new(rtr_id, iface, 0); 188e9496ee4Sclaudio /* set neighbor parameters */ 189e9496ee4Sclaudio nbr->dr.s_addr = hello.d_rtr; 190e9496ee4Sclaudio nbr->bdr.s_addr = hello.bd_rtr; 191e9496ee4Sclaudio nbr->priority = hello.rtr_priority; 1922fa37528Sremi /* XXX neighbor address shouldn't be stored on virtual links */ 1932fa37528Sremi nbr->addr.s_addr = src.s_addr; 194ef209401Sremi ospfe_imsg_compose_rde(IMSG_NEIGHBOR_ADDR, nbr->peerid, 0, 195ef209401Sremi &src, sizeof(src)); 196e9496ee4Sclaudio } 197ebc9e90aSnorby 1982fa37528Sremi if (nbr->addr.s_addr != src.s_addr) { 1992fa37528Sremi log_warnx("%s: neighbor ID %s changed its IP address", 2002fa37528Sremi __func__, inet_ntoa(nbr->id)); 201204df0f8Sclaudio nbr->addr.s_addr = src.s_addr; 202ef209401Sremi ospfe_imsg_compose_rde(IMSG_NEIGHBOR_ADDR, nbr->peerid, 0, 203ef209401Sremi &src, sizeof(src)); 2042fa37528Sremi } 2052fa37528Sremi 206204df0f8Sclaudio nbr->options = hello.opts; 207204df0f8Sclaudio 208204df0f8Sclaudio nbr_fsm(nbr, NBR_EVT_HELLO_RCVD); 209204df0f8Sclaudio 210204df0f8Sclaudio while (len >= sizeof(nbr_id)) { 211204df0f8Sclaudio memcpy(&nbr_id, buf, sizeof(nbr_id)); 212f12637e5Smsf if (nbr_id == ospfe_router_id()) { 213204df0f8Sclaudio /* seen myself */ 214bd0b23eeSclaudio if (nbr->state & NBR_STA_PRELIM) { 215e9496ee4Sclaudio nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD); 216bd0b23eeSclaudio nbr_change = 1; 217bd0b23eeSclaudio } 218204df0f8Sclaudio break; 219204df0f8Sclaudio } 220204df0f8Sclaudio buf += sizeof(nbr_id); 221204df0f8Sclaudio len -= sizeof(nbr_id); 222204df0f8Sclaudio } 223204df0f8Sclaudio 224204df0f8Sclaudio if (len == 0) { 225204df0f8Sclaudio nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD); 226204df0f8Sclaudio /* set neighbor parameters */ 227204df0f8Sclaudio nbr->dr.s_addr = hello.d_rtr; 228204df0f8Sclaudio nbr->bdr.s_addr = hello.bd_rtr; 229204df0f8Sclaudio nbr->priority = hello.rtr_priority; 230204df0f8Sclaudio return; 231204df0f8Sclaudio } 232204df0f8Sclaudio 233204df0f8Sclaudio if (nbr->priority != hello.rtr_priority) { 234204df0f8Sclaudio nbr->priority = hello.rtr_priority; 235204df0f8Sclaudio nbr_change = 1; 236204df0f8Sclaudio } 237204df0f8Sclaudio 2385ec720d9Sclaudio if (iface->state & IF_STA_WAITING && 239581e0255Sclaudio hello.d_rtr == nbr->addr.s_addr && hello.bd_rtr == 0) 240204df0f8Sclaudio if_fsm(iface, IF_EVT_BACKUP_SEEN); 241e9496ee4Sclaudio 242e9496ee4Sclaudio if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->addr.s_addr) { 243e9496ee4Sclaudio /* 244e9496ee4Sclaudio * In case we see the BDR make sure that the DR is around 245e9496ee4Sclaudio * with a bidirectional (2_WAY or better) connection 246e9496ee4Sclaudio */ 247e9496ee4Sclaudio LIST_FOREACH(dr, &iface->nbr_list, entry) 248e9496ee4Sclaudio if (hello.d_rtr == dr->addr.s_addr && 249581e0255Sclaudio dr->state & NBR_STA_BIDIR) 250e9496ee4Sclaudio if_fsm(iface, IF_EVT_BACKUP_SEEN); 251e9496ee4Sclaudio } 252204df0f8Sclaudio 253204df0f8Sclaudio if ((nbr->addr.s_addr == nbr->dr.s_addr && 254204df0f8Sclaudio nbr->addr.s_addr != hello.d_rtr) || 255204df0f8Sclaudio (nbr->addr.s_addr != nbr->dr.s_addr && 256204df0f8Sclaudio nbr->addr.s_addr == hello.d_rtr)) 257204df0f8Sclaudio /* neighbor changed from or to DR */ 258204df0f8Sclaudio nbr_change = 1; 259204df0f8Sclaudio if ((nbr->addr.s_addr == nbr->bdr.s_addr && 260204df0f8Sclaudio nbr->addr.s_addr != hello.bd_rtr) || 261204df0f8Sclaudio (nbr->addr.s_addr != nbr->bdr.s_addr && 262204df0f8Sclaudio nbr->addr.s_addr == hello.bd_rtr)) 263204df0f8Sclaudio /* neighbor changed from or to BDR */ 264204df0f8Sclaudio nbr_change = 1; 265204df0f8Sclaudio 266204df0f8Sclaudio nbr->dr.s_addr = hello.d_rtr; 267204df0f8Sclaudio nbr->bdr.s_addr = hello.bd_rtr; 268204df0f8Sclaudio 269204df0f8Sclaudio if (nbr_change) 270204df0f8Sclaudio if_fsm(iface, IF_EVT_NBR_CHNG); 271204df0f8Sclaudio 272204df0f8Sclaudio /* TODO NBMA needs some special handling */ 273204df0f8Sclaudio } 274