1 /* $OpenBSD: hello.c,v 1.20 2011/03/08 10:56:02 claudio 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 <sys/socket.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <event.h> 29 30 #include "ospfd.h" 31 #include "ospf.h" 32 #include "log.h" 33 #include "ospfe.h" 34 35 extern struct ospfd_conf *oeconf; 36 37 /* hello packet handling */ 38 int 39 send_hello(struct iface *iface) 40 { 41 struct sockaddr_in dst; 42 struct hello_hdr hello; 43 struct nbr *nbr; 44 struct ibuf *buf; 45 int ret; 46 47 dst.sin_family = AF_INET; 48 dst.sin_len = sizeof(struct sockaddr_in); 49 50 switch (iface->type) { 51 case IF_TYPE_POINTOPOINT: 52 case IF_TYPE_BROADCAST: 53 inet_aton(AllSPFRouters, &dst.sin_addr); 54 break; 55 case IF_TYPE_NBMA: 56 case IF_TYPE_POINTOMULTIPOINT: 57 log_debug("send_hello: type %s not supported, interface %s", 58 if_type_name(iface->type), iface->name); 59 return (-1); 60 case IF_TYPE_VIRTUALLINK: 61 dst.sin_addr = iface->dst; 62 break; 63 default: 64 fatalx("send_hello: unknown interface type"); 65 } 66 67 if ((buf = ibuf_dynamic(PKG_DEF_SIZE, 68 IP_MAXPACKET - sizeof(struct ip))) == NULL) 69 fatal("send_hello"); 70 71 /* OSPF header */ 72 if (gen_ospf_hdr(buf, iface, PACKET_TYPE_HELLO)) 73 goto fail; 74 75 /* hello header */ 76 hello.mask = iface->mask.s_addr; 77 hello.hello_interval = htons(iface->hello_interval); 78 hello.opts = area_ospf_options(iface->area); 79 hello.rtr_priority = iface->priority; 80 hello.rtr_dead_interval = htonl(iface->dead_interval); 81 82 if (iface->dr) { 83 hello.d_rtr = iface->dr->addr.s_addr; 84 iface->self->dr.s_addr = iface->dr->addr.s_addr; 85 } else 86 hello.d_rtr = 0; 87 if (iface->bdr) { 88 hello.bd_rtr = iface->bdr->addr.s_addr; 89 iface->self->bdr.s_addr = iface->bdr->addr.s_addr; 90 } else 91 hello.bd_rtr = 0; 92 93 if (ibuf_add(buf, &hello, sizeof(hello))) 94 goto fail; 95 96 /* active neighbor(s) */ 97 LIST_FOREACH(nbr, &iface->nbr_list, entry) { 98 if ((nbr->state >= NBR_STA_INIT) && (nbr != iface->self)) 99 if (ibuf_add(buf, &nbr->id, sizeof(nbr->id))) 100 goto fail; 101 } 102 103 /* update authentication and calculate checksum */ 104 if (auth_gen(buf, iface)) 105 goto fail; 106 107 ret = send_packet(iface, buf, &dst); 108 ibuf_free(buf); 109 return (ret); 110 fail: 111 log_warn("send_hello"); 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 nbr_change = 1; 193 } 194 195 /* actually the neighbor address shouldn't be stored on virtual links */ 196 nbr->addr.s_addr = src.s_addr; 197 nbr->options = hello.opts; 198 199 nbr_fsm(nbr, NBR_EVT_HELLO_RCVD); 200 201 while (len >= sizeof(nbr_id)) { 202 memcpy(&nbr_id, buf, sizeof(nbr_id)); 203 if (nbr_id == ospfe_router_id()) { 204 /* seen myself */ 205 if (nbr->state & NBR_STA_PRELIM) 206 nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD); 207 break; 208 } 209 buf += sizeof(nbr_id); 210 len -= sizeof(nbr_id); 211 } 212 213 if (len == 0) { 214 nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD); 215 /* set neighbor parameters */ 216 nbr->dr.s_addr = hello.d_rtr; 217 nbr->bdr.s_addr = hello.bd_rtr; 218 nbr->priority = hello.rtr_priority; 219 return; 220 } 221 222 if (nbr->priority != hello.rtr_priority) { 223 nbr->priority = hello.rtr_priority; 224 nbr_change = 1; 225 } 226 227 if (iface->state & IF_STA_WAITING && 228 hello.d_rtr == nbr->addr.s_addr && hello.bd_rtr == 0) 229 if_fsm(iface, IF_EVT_BACKUP_SEEN); 230 231 if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->addr.s_addr) { 232 /* 233 * In case we see the BDR make sure that the DR is around 234 * with a bidirectional (2_WAY or better) connection 235 */ 236 LIST_FOREACH(dr, &iface->nbr_list, entry) 237 if (hello.d_rtr == dr->addr.s_addr && 238 dr->state & NBR_STA_BIDIR) 239 if_fsm(iface, IF_EVT_BACKUP_SEEN); 240 } 241 242 if ((nbr->addr.s_addr == nbr->dr.s_addr && 243 nbr->addr.s_addr != hello.d_rtr) || 244 (nbr->addr.s_addr != nbr->dr.s_addr && 245 nbr->addr.s_addr == hello.d_rtr)) 246 /* neighbor changed from or to DR */ 247 nbr_change = 1; 248 if ((nbr->addr.s_addr == nbr->bdr.s_addr && 249 nbr->addr.s_addr != hello.bd_rtr) || 250 (nbr->addr.s_addr != nbr->bdr.s_addr && 251 nbr->addr.s_addr == hello.bd_rtr)) 252 /* neighbor changed from or to BDR */ 253 nbr_change = 1; 254 255 nbr->dr.s_addr = hello.d_rtr; 256 nbr->bdr.s_addr = hello.bd_rtr; 257 258 if (nbr_change) 259 if_fsm(iface, IF_EVT_NBR_CHNG); 260 261 /* TODO NBMA needs some special handling */ 262 } 263