1 /* $OpenBSD: hello.c,v 1.21 2014/11/18 20:54:29 krw 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 int ret; 45 46 dst.sin_family = AF_INET; 47 dst.sin_len = sizeof(struct sockaddr_in); 48 49 switch (iface->type) { 50 case IF_TYPE_POINTOPOINT: 51 case IF_TYPE_BROADCAST: 52 inet_aton(AllSPFRouters, &dst.sin_addr); 53 break; 54 case IF_TYPE_NBMA: 55 case IF_TYPE_POINTOMULTIPOINT: 56 log_debug("send_hello: type %s not supported, interface %s", 57 if_type_name(iface->type), iface->name); 58 return (-1); 59 case IF_TYPE_VIRTUALLINK: 60 dst.sin_addr = iface->dst; 61 break; 62 default: 63 fatalx("send_hello: unknown interface type"); 64 } 65 66 if ((buf = ibuf_dynamic(PKG_DEF_SIZE, 67 IP_MAXPACKET - sizeof(struct ip))) == NULL) 68 fatal("send_hello"); 69 70 /* OSPF header */ 71 if (gen_ospf_hdr(buf, iface, PACKET_TYPE_HELLO)) 72 goto fail; 73 74 /* hello header */ 75 hello.mask = iface->mask.s_addr; 76 hello.hello_interval = htons(iface->hello_interval); 77 hello.opts = area_ospf_options(iface->area); 78 hello.rtr_priority = iface->priority; 79 hello.rtr_dead_interval = htonl(iface->dead_interval); 80 81 if (iface->dr) { 82 hello.d_rtr = iface->dr->addr.s_addr; 83 iface->self->dr.s_addr = iface->dr->addr.s_addr; 84 } else 85 hello.d_rtr = 0; 86 if (iface->bdr) { 87 hello.bd_rtr = iface->bdr->addr.s_addr; 88 iface->self->bdr.s_addr = iface->bdr->addr.s_addr; 89 } else 90 hello.bd_rtr = 0; 91 92 if (ibuf_add(buf, &hello, sizeof(hello))) 93 goto fail; 94 95 /* active neighbor(s) */ 96 LIST_FOREACH(nbr, &iface->nbr_list, entry) { 97 if ((nbr->state >= NBR_STA_INIT) && (nbr != iface->self)) 98 if (ibuf_add(buf, &nbr->id, sizeof(nbr->id))) 99 goto fail; 100 } 101 102 /* update authentication and calculate checksum */ 103 if (auth_gen(buf, iface)) 104 goto fail; 105 106 ret = send_packet(iface, buf, &dst); 107 ibuf_free(buf); 108 return (ret); 109 fail: 110 log_warn("send_hello"); 111 ibuf_free(buf); 112 return (-1); 113 } 114 115 void 116 recv_hello(struct iface *iface, struct in_addr src, u_int32_t rtr_id, char *buf, 117 u_int16_t len) 118 { 119 struct hello_hdr hello; 120 struct nbr *nbr = NULL, *dr; 121 u_int32_t nbr_id; 122 int nbr_change = 0; 123 124 if (len < sizeof(hello) || (len & 0x03)) { 125 log_warnx("recv_hello: bad packet size, interface %s", 126 iface->name); 127 return; 128 } 129 130 memcpy(&hello, buf, sizeof(hello)); 131 buf += sizeof(hello); 132 len -= sizeof(hello); 133 134 if (iface->type != IF_TYPE_POINTOPOINT && 135 iface->type != IF_TYPE_VIRTUALLINK) 136 if (hello.mask != iface->mask.s_addr) { 137 log_warnx("recv_hello: invalid netmask, interface %s", 138 iface->name); 139 return; 140 } 141 142 if (ntohs(hello.hello_interval) != iface->hello_interval) { 143 log_warnx("recv_hello: invalid hello-interval %d, " 144 "interface %s", ntohs(hello.hello_interval), 145 iface->name); 146 return; 147 } 148 149 if (ntohl(hello.rtr_dead_interval) != iface->dead_interval) { 150 log_warnx("recv_hello: invalid router-dead-interval %d, " 151 "interface %s", ntohl(hello.rtr_dead_interval), 152 iface->name); 153 return; 154 } 155 156 if ((hello.opts & OSPF_OPTION_E && iface->area->stub) || 157 ((hello.opts & OSPF_OPTION_E) == 0 && !iface->area->stub)) { 158 log_warnx("recv_hello: ExternalRoutingCapability mismatch, " 159 "interface %s", iface->name); 160 return; 161 } 162 163 /* 164 * Match router-id, in case of conflict moan and ignore hello. 165 * Only the router-id is compared since the source IP on NBMA, 166 * broadcast and point-to-multipoint interfaces was already 167 * compared in find_iface() and only IPs in the same subnet 168 * are accepted. This is not excatly what the RFC specifies 169 * but works far better. 170 */ 171 LIST_FOREACH(nbr, &iface->nbr_list, entry) { 172 if (nbr == iface->self) { 173 if (nbr->id.s_addr == rtr_id) { 174 log_warnx("recv_hello: Router-ID collision on " 175 "interface %s neighbor IP %s", iface->name, 176 inet_ntoa(src)); 177 return; 178 } 179 continue; 180 } 181 if (nbr->id.s_addr == rtr_id) 182 break; 183 } 184 185 if (!nbr) { 186 nbr = nbr_new(rtr_id, iface, 0); 187 /* set neighbor parameters */ 188 nbr->dr.s_addr = hello.d_rtr; 189 nbr->bdr.s_addr = hello.bd_rtr; 190 nbr->priority = hello.rtr_priority; 191 nbr_change = 1; 192 } 193 194 /* actually the neighbor address shouldn't be stored on virtual links */ 195 nbr->addr.s_addr = src.s_addr; 196 nbr->options = hello.opts; 197 198 nbr_fsm(nbr, NBR_EVT_HELLO_RCVD); 199 200 while (len >= sizeof(nbr_id)) { 201 memcpy(&nbr_id, buf, sizeof(nbr_id)); 202 if (nbr_id == ospfe_router_id()) { 203 /* seen myself */ 204 if (nbr->state & NBR_STA_PRELIM) 205 nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD); 206 break; 207 } 208 buf += sizeof(nbr_id); 209 len -= sizeof(nbr_id); 210 } 211 212 if (len == 0) { 213 nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD); 214 /* set neighbor parameters */ 215 nbr->dr.s_addr = hello.d_rtr; 216 nbr->bdr.s_addr = hello.bd_rtr; 217 nbr->priority = hello.rtr_priority; 218 return; 219 } 220 221 if (nbr->priority != hello.rtr_priority) { 222 nbr->priority = hello.rtr_priority; 223 nbr_change = 1; 224 } 225 226 if (iface->state & IF_STA_WAITING && 227 hello.d_rtr == nbr->addr.s_addr && hello.bd_rtr == 0) 228 if_fsm(iface, IF_EVT_BACKUP_SEEN); 229 230 if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->addr.s_addr) { 231 /* 232 * In case we see the BDR make sure that the DR is around 233 * with a bidirectional (2_WAY or better) connection 234 */ 235 LIST_FOREACH(dr, &iface->nbr_list, entry) 236 if (hello.d_rtr == dr->addr.s_addr && 237 dr->state & NBR_STA_BIDIR) 238 if_fsm(iface, IF_EVT_BACKUP_SEEN); 239 } 240 241 if ((nbr->addr.s_addr == nbr->dr.s_addr && 242 nbr->addr.s_addr != hello.d_rtr) || 243 (nbr->addr.s_addr != nbr->dr.s_addr && 244 nbr->addr.s_addr == hello.d_rtr)) 245 /* neighbor changed from or to DR */ 246 nbr_change = 1; 247 if ((nbr->addr.s_addr == nbr->bdr.s_addr && 248 nbr->addr.s_addr != hello.bd_rtr) || 249 (nbr->addr.s_addr != nbr->bdr.s_addr && 250 nbr->addr.s_addr == hello.bd_rtr)) 251 /* neighbor changed from or to BDR */ 252 nbr_change = 1; 253 254 nbr->dr.s_addr = hello.d_rtr; 255 nbr->bdr.s_addr = hello.bd_rtr; 256 257 if (nbr_change) 258 if_fsm(iface, IF_EVT_NBR_CHNG); 259 260 /* TODO NBMA needs some special handling */ 261 } 262