1 /* $OpenBSD: hello.c,v 1.25 2019/11/19 09:55:55 remi Exp $ */ 2 3 /* 4 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 5 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/socket.h> 22 #include <netinet/in.h> 23 #include <arpa/inet.h> 24 #include <sys/time.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <event.h> 28 29 #include "ospfd.h" 30 #include "ospf.h" 31 #include "log.h" 32 #include "ospfe.h" 33 34 extern struct ospfd_conf *oeconf; 35 36 /* hello packet handling */ 37 int 38 send_hello(struct iface *iface) 39 { 40 struct sockaddr_in dst; 41 struct hello_hdr hello; 42 struct nbr *nbr; 43 struct ibuf *buf; 44 45 dst.sin_family = AF_INET; 46 dst.sin_len = sizeof(struct sockaddr_in); 47 48 switch (iface->type) { 49 case IF_TYPE_POINTOPOINT: 50 case IF_TYPE_BROADCAST: 51 inet_aton(AllSPFRouters, &dst.sin_addr); 52 break; 53 case IF_TYPE_NBMA: 54 case IF_TYPE_POINTOMULTIPOINT: 55 log_debug("send_hello: type %s not supported, interface %s", 56 if_type_name(iface->type), iface->name); 57 return (-1); 58 case IF_TYPE_VIRTUALLINK: 59 dst.sin_addr = iface->dst; 60 break; 61 default: 62 fatalx("send_hello: unknown interface type"); 63 } 64 65 if ((buf = ibuf_dynamic(PKG_DEF_SIZE, 66 IP_MAXPACKET - sizeof(struct ip))) == NULL) 67 fatal("send_hello"); 68 69 /* OSPF header */ 70 if (gen_ospf_hdr(buf, iface, PACKET_TYPE_HELLO)) 71 goto fail; 72 73 /* hello header */ 74 hello.mask = iface->mask.s_addr; 75 hello.hello_interval = htons(iface->hello_interval); 76 hello.opts = area_ospf_options(iface->area); 77 hello.rtr_priority = iface->priority; 78 hello.rtr_dead_interval = htonl(iface->dead_interval); 79 80 if (iface->dr) { 81 hello.d_rtr = iface->dr->addr.s_addr; 82 iface->self->dr.s_addr = iface->dr->addr.s_addr; 83 } else 84 hello.d_rtr = 0; 85 if (iface->bdr) { 86 hello.bd_rtr = iface->bdr->addr.s_addr; 87 iface->self->bdr.s_addr = iface->bdr->addr.s_addr; 88 } else 89 hello.bd_rtr = 0; 90 91 if (ibuf_add(buf, &hello, sizeof(hello))) 92 goto fail; 93 94 /* active neighbor(s) */ 95 LIST_FOREACH(nbr, &iface->nbr_list, entry) { 96 if ((nbr->state >= NBR_STA_INIT) && (nbr != iface->self)) 97 if (ibuf_add(buf, &nbr->id, sizeof(nbr->id))) 98 goto fail; 99 } 100 101 /* update authentication and calculate checksum */ 102 if (auth_gen(buf, iface)) 103 goto fail; 104 105 if (send_packet(iface, buf, &dst) == -1) 106 goto fail; 107 108 ibuf_free(buf); 109 return (0); 110 fail: 111 log_warn("%s", __func__); 112 ibuf_free(buf); 113 return (-1); 114 } 115 116 void 117 recv_hello(struct iface *iface, struct in_addr src, u_int32_t rtr_id, char *buf, 118 u_int16_t len) 119 { 120 struct hello_hdr hello; 121 struct nbr *nbr = NULL, *dr; 122 u_int32_t nbr_id; 123 int nbr_change = 0; 124 125 if (len < sizeof(hello) || (len & 0x03)) { 126 log_warnx("recv_hello: bad packet size, interface %s", 127 iface->name); 128 return; 129 } 130 131 memcpy(&hello, buf, sizeof(hello)); 132 buf += sizeof(hello); 133 len -= sizeof(hello); 134 135 if (iface->type != IF_TYPE_POINTOPOINT && 136 iface->type != IF_TYPE_VIRTUALLINK) 137 if (hello.mask != iface->mask.s_addr) { 138 log_warnx("recv_hello: invalid netmask, interface %s", 139 iface->name); 140 return; 141 } 142 143 if (ntohs(hello.hello_interval) != iface->hello_interval) { 144 log_warnx("recv_hello: invalid hello-interval %d, " 145 "interface %s", ntohs(hello.hello_interval), 146 iface->name); 147 return; 148 } 149 150 if (ntohl(hello.rtr_dead_interval) != iface->dead_interval) { 151 log_warnx("recv_hello: invalid router-dead-interval %d, " 152 "interface %s", ntohl(hello.rtr_dead_interval), 153 iface->name); 154 return; 155 } 156 157 if ((hello.opts & OSPF_OPTION_E && iface->area->stub) || 158 ((hello.opts & OSPF_OPTION_E) == 0 && !iface->area->stub)) { 159 log_warnx("recv_hello: ExternalRoutingCapability mismatch, " 160 "interface %s", iface->name); 161 return; 162 } 163 164 /* 165 * Match router-id, in case of conflict moan and ignore hello. 166 * Only the router-id is compared since the source IP on NBMA, 167 * broadcast and point-to-multipoint interfaces was already 168 * compared in find_iface() and only IPs in the same subnet 169 * are accepted. This is not excatly what the RFC specifies 170 * but works far better. 171 */ 172 LIST_FOREACH(nbr, &iface->nbr_list, entry) { 173 if (nbr == iface->self) { 174 if (nbr->id.s_addr == rtr_id) { 175 log_warnx("recv_hello: Router-ID collision on " 176 "interface %s neighbor IP %s", iface->name, 177 inet_ntoa(src)); 178 return; 179 } 180 continue; 181 } 182 if (nbr->id.s_addr == rtr_id) 183 break; 184 } 185 186 if (!nbr) { 187 nbr = nbr_new(rtr_id, iface, 0); 188 /* set neighbor parameters */ 189 nbr->dr.s_addr = hello.d_rtr; 190 nbr->bdr.s_addr = hello.bd_rtr; 191 nbr->priority = hello.rtr_priority; 192 /* XXX neighbor address shouldn't be stored on virtual links */ 193 nbr->addr.s_addr = src.s_addr; 194 ospfe_imsg_compose_rde(IMSG_NEIGHBOR_ADDR, nbr->peerid, 0, 195 &src, sizeof(src)); 196 } 197 198 if (nbr->addr.s_addr != src.s_addr) { 199 log_warnx("%s: neighbor ID %s changed its IP address", 200 __func__, inet_ntoa(nbr->id)); 201 nbr->addr.s_addr = src.s_addr; 202 ospfe_imsg_compose_rde(IMSG_NEIGHBOR_ADDR, nbr->peerid, 0, 203 &src, sizeof(src)); 204 } 205 206 nbr->options = hello.opts; 207 208 nbr_fsm(nbr, NBR_EVT_HELLO_RCVD); 209 210 while (len >= sizeof(nbr_id)) { 211 memcpy(&nbr_id, buf, sizeof(nbr_id)); 212 if (nbr_id == ospfe_router_id()) { 213 /* seen myself */ 214 if (nbr->state & NBR_STA_PRELIM) { 215 nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD); 216 nbr_change = 1; 217 } 218 break; 219 } 220 buf += sizeof(nbr_id); 221 len -= sizeof(nbr_id); 222 } 223 224 if (len == 0) { 225 nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD); 226 /* set neighbor parameters */ 227 nbr->dr.s_addr = hello.d_rtr; 228 nbr->bdr.s_addr = hello.bd_rtr; 229 nbr->priority = hello.rtr_priority; 230 return; 231 } 232 233 if (nbr->priority != hello.rtr_priority) { 234 nbr->priority = hello.rtr_priority; 235 nbr_change = 1; 236 } 237 238 if (iface->state & IF_STA_WAITING && 239 hello.d_rtr == nbr->addr.s_addr && hello.bd_rtr == 0) 240 if_fsm(iface, IF_EVT_BACKUP_SEEN); 241 242 if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->addr.s_addr) { 243 /* 244 * In case we see the BDR make sure that the DR is around 245 * with a bidirectional (2_WAY or better) connection 246 */ 247 LIST_FOREACH(dr, &iface->nbr_list, entry) 248 if (hello.d_rtr == dr->addr.s_addr && 249 dr->state & NBR_STA_BIDIR) 250 if_fsm(iface, IF_EVT_BACKUP_SEEN); 251 } 252 253 if ((nbr->addr.s_addr == nbr->dr.s_addr && 254 nbr->addr.s_addr != hello.d_rtr) || 255 (nbr->addr.s_addr != nbr->dr.s_addr && 256 nbr->addr.s_addr == hello.d_rtr)) 257 /* neighbor changed from or to DR */ 258 nbr_change = 1; 259 if ((nbr->addr.s_addr == nbr->bdr.s_addr && 260 nbr->addr.s_addr != hello.bd_rtr) || 261 (nbr->addr.s_addr != nbr->bdr.s_addr && 262 nbr->addr.s_addr == hello.bd_rtr)) 263 /* neighbor changed from or to BDR */ 264 nbr_change = 1; 265 266 nbr->dr.s_addr = hello.d_rtr; 267 nbr->bdr.s_addr = hello.bd_rtr; 268 269 if (nbr_change) 270 if_fsm(iface, IF_EVT_NBR_CHNG); 271 272 /* TODO NBMA needs some special handling */ 273 } 274