1 /* $OpenBSD: hello.c,v 1.17 2014/11/18 20:54:28 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 "ospf6d.h" 30 #include "ospf6.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 in6_addr dst; 41 struct hello_hdr hello; 42 struct nbr *nbr; 43 struct ibuf *buf; 44 int ret; 45 u_int32_t opts; 46 47 switch (iface->type) { 48 case IF_TYPE_POINTOPOINT: 49 case IF_TYPE_BROADCAST: 50 inet_pton(AF_INET6, AllSPFRouters, &dst); 51 break; 52 case IF_TYPE_NBMA: 53 case IF_TYPE_POINTOMULTIPOINT: 54 log_debug("send_hello: type %s not supported, interface %s", 55 if_type_name(iface->type), iface->name); 56 return (-1); 57 case IF_TYPE_VIRTUALLINK: 58 dst = iface->dst; 59 break; 60 default: 61 fatalx("send_hello: unknown interface type"); 62 } 63 64 /* XXX IBUF_READ_SIZE */ 65 if ((buf = ibuf_dynamic(PKG_DEF_SIZE, IBUF_READ_SIZE)) == NULL) 66 fatal("send_hello"); 67 68 /* OSPF header */ 69 if (gen_ospf_hdr(buf, iface, PACKET_TYPE_HELLO)) 70 goto fail; 71 72 /* hello header */ 73 hello.iface_id = htonl(iface->ifindex); 74 LSA_24_SETHI(hello.opts, iface->priority); 75 opts = area_ospf_options(area_find(oeconf, iface->area_id)); 76 LSA_24_SETLO(hello.opts, opts); 77 hello.opts = htonl(hello.opts); 78 79 hello.hello_interval = htons(iface->hello_interval); 80 hello.rtr_dead_interval = htons(iface->dead_interval); 81 82 if (iface->dr) { 83 hello.d_rtr = iface->dr->id.s_addr; 84 iface->self->dr.s_addr = iface->dr->id.s_addr; 85 } else 86 hello.d_rtr = 0; 87 if (iface->bdr) { 88 hello.bd_rtr = iface->bdr->id.s_addr; 89 iface->self->bdr.s_addr = iface->bdr->id.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 /* calculate checksum */ 104 if (upd_ospf_hdr(buf, iface)) 105 goto fail; 106 107 ret = send_packet(iface, buf->buf, buf->wpos, &dst); 108 109 ibuf_free(buf); 110 return (ret); 111 fail: 112 log_warn("send_hello"); 113 ibuf_free(buf); 114 return (-1); 115 } 116 117 void 118 recv_hello(struct iface *iface, struct in6_addr *src, u_int32_t rtr_id, 119 char *buf, u_int16_t len) 120 { 121 struct hello_hdr hello; 122 struct nbr *nbr = NULL, *dr; 123 struct area *area; 124 u_int32_t nbr_id, opts; 125 int nbr_change = 0; 126 127 if (len < sizeof(hello) || (len & 0x03)) { 128 log_warnx("recv_hello: bad packet size, interface %s", 129 iface->name); 130 return; 131 } 132 133 memcpy(&hello, buf, sizeof(hello)); 134 buf += sizeof(hello); 135 len -= sizeof(hello); 136 137 if (ntohs(hello.hello_interval) != iface->hello_interval) { 138 log_warnx("recv_hello: invalid hello-interval %d, " 139 "interface %s", ntohs(hello.hello_interval), 140 iface->name); 141 return; 142 } 143 144 if (ntohs(hello.rtr_dead_interval) != iface->dead_interval) { 145 log_warnx("recv_hello: invalid router-dead-interval %d, " 146 "interface %s", ntohl(hello.rtr_dead_interval), 147 iface->name); 148 return; 149 } 150 151 if ((area = area_find(oeconf, iface->area_id)) == NULL) 152 fatalx("interface lost area"); 153 154 opts = LSA_24_GETLO(ntohl(hello.opts)); 155 if ((opts & OSPF_OPTION_E && area->stub) || 156 ((opts & OSPF_OPTION_E) == 0 && !area->stub)) { 157 log_warnx("recv_hello: ExternalRoutingCapability mismatch, " 158 "interface %s", iface->name); 159 return; 160 } 161 162 /* match router-id */ 163 LIST_FOREACH(nbr, &iface->nbr_list, entry) { 164 if (nbr == iface->self) 165 continue; 166 if (nbr->id.s_addr == rtr_id) 167 break; 168 } 169 170 if (!nbr) { 171 nbr = nbr_new(rtr_id, iface, ntohl(hello.iface_id), 0, src); 172 /* set neighbor parameters */ 173 nbr->dr.s_addr = hello.d_rtr; 174 nbr->bdr.s_addr = hello.bd_rtr; 175 nbr->priority = LSA_24_GETHI(ntohl(hello.opts)); 176 nbr_change = 1; 177 } 178 179 /* actually the neighbor address shouldn't be stored on virtual links */ 180 nbr->addr = *src; 181 nbr->options = opts; 182 183 nbr_fsm(nbr, NBR_EVT_HELLO_RCVD); 184 185 while (len >= sizeof(nbr_id)) { 186 memcpy(&nbr_id, buf, sizeof(nbr_id)); 187 if (nbr_id == ospfe_router_id()) { 188 /* seen myself */ 189 if (nbr->state & NBR_STA_PRELIM) 190 nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD); 191 break; 192 } 193 buf += sizeof(nbr_id); 194 len -= sizeof(nbr_id); 195 } 196 197 if (len == 0) { 198 nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD); 199 /* set neighbor parameters */ 200 nbr->dr.s_addr = hello.d_rtr; 201 nbr->bdr.s_addr = hello.bd_rtr; 202 nbr->priority = LSA_24_GETHI(ntohl(hello.opts)); 203 return; 204 } 205 206 if (nbr->priority != LSA_24_GETHI(ntohl(hello.opts))) { 207 nbr->priority = LSA_24_GETHI(ntohl(hello.opts)); 208 nbr_change = 1; 209 } 210 211 if (iface->state & IF_STA_WAITING && 212 hello.d_rtr == nbr->id.s_addr && hello.bd_rtr == 0) 213 if_fsm(iface, IF_EVT_BACKUP_SEEN); 214 215 if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->id.s_addr) { 216 /* 217 * In case we see the BDR make sure that the DR is around 218 * with a bidirectional (2_WAY or better) connection 219 */ 220 LIST_FOREACH(dr, &iface->nbr_list, entry) 221 if (hello.d_rtr == dr->id.s_addr && 222 dr->state & NBR_STA_BIDIR) 223 if_fsm(iface, IF_EVT_BACKUP_SEEN); 224 } 225 226 if ((nbr->id.s_addr == nbr->dr.s_addr && 227 nbr->id.s_addr != hello.d_rtr) || 228 (nbr->id.s_addr != nbr->dr.s_addr && 229 nbr->id.s_addr == hello.d_rtr)) 230 /* neighbor changed from or to DR */ 231 nbr_change = 1; 232 if ((nbr->id.s_addr == nbr->bdr.s_addr && 233 nbr->id.s_addr != hello.bd_rtr) || 234 (nbr->id.s_addr != nbr->bdr.s_addr && 235 nbr->id.s_addr == hello.bd_rtr)) 236 /* neighbor changed from or to BDR */ 237 nbr_change = 1; 238 239 nbr->dr.s_addr = hello.d_rtr; 240 nbr->bdr.s_addr = hello.bd_rtr; 241 242 if (nbr_change) 243 if_fsm(iface, IF_EVT_NBR_CHNG); 244 245 /* TODO NBMA needs some special handling */ 246 } 247