xref: /openbsd-src/usr.sbin/ospf6d/hello.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /*	$OpenBSD: hello.c,v 1.9 2007/12/13 08:54:05 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 "ospf6d.h"
31 #include "ospf6.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 in6_addr		 dst;
42 	struct hello_hdr	 hello;
43 	struct nbr		*nbr;
44 	struct buf		*buf;
45 	int			 ret;
46 	u_int32_t		 opts;
47 
48 	switch (iface->type) {
49 	case IF_TYPE_POINTOPOINT:
50 	case IF_TYPE_BROADCAST:
51 		inet_pton(AF_INET6, AllSPFRouters, &dst);
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 = iface->dst;
60 		break;
61 	default:
62 		fatalx("send_hello: unknown interface type");
63 	}
64 
65 	/* XXX READ_BUF_SIZE */
66 	if ((buf = buf_dynamic(PKG_DEF_SIZE, READ_BUF_SIZE)) == 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.iface_id = iface->ifindex;
75 	hello.rtr_priority = iface->priority;
76 
77 	opts = ntohl(area_ospf_options(area_find(oeconf, iface->area_id)));
78 	hello.opts1 = (opts >> 16) & 0xff;
79 	hello.opts2 = (opts >> 8) & 0xff;
80 	hello.opts3 = opts & 0xff;
81 
82 	hello.hello_interval = htons(iface->hello_interval);
83 	hello.rtr_dead_interval = htons(iface->dead_interval);
84 
85 	if (iface->dr) {
86 		hello.d_rtr = iface->dr->id.s_addr;
87 		iface->self->dr.s_addr = iface->dr->id.s_addr;
88 	} else
89 		hello.d_rtr = 0;
90 	if (iface->bdr) {
91 		hello.bd_rtr = iface->bdr->id.s_addr;
92 		iface->self->bdr.s_addr = iface->bdr->id.s_addr;
93 	} else
94 		hello.bd_rtr = 0;
95 
96 	if (buf_add(buf, &hello, sizeof(hello)))
97 		goto fail;
98 
99 	/* active neighbor(s) */
100 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
101 		if ((nbr->state >= NBR_STA_INIT) && (nbr != iface->self))
102 			if (buf_add(buf, &nbr->id, sizeof(nbr->id)))
103 				goto fail;
104 	}
105 
106 	/* calculate checksum */
107 	if (upd_ospf_hdr(buf, iface))
108 		goto fail;
109 
110 	ret = send_packet(iface, buf->buf, buf->wpos, &dst);
111 
112 	buf_free(buf);
113 	return (ret);
114 fail:
115 	log_warn("send_hello");
116 	buf_free(buf);
117 	return (-1);
118 }
119 
120 void
121 recv_hello(struct iface *iface, struct in6_addr *src, u_int32_t rtr_id,
122     char *buf, u_int16_t len)
123 {
124 	struct hello_hdr	 hello;
125 	struct nbr		*nbr = NULL, *dr;
126 	struct area		*area;
127 	u_int32_t		 nbr_id;
128 	int			 nbr_change = 0;
129 
130 	if (len < sizeof(hello) && (len & 0x03)) {
131 		log_warnx("recv_hello: bad packet size, interface %s",
132 		    iface->name);
133 		return;
134 	}
135 
136 	memcpy(&hello, buf, sizeof(hello));
137 	buf += sizeof(hello);
138 	len -= sizeof(hello);
139 
140 	if (ntohs(hello.hello_interval) != iface->hello_interval) {
141 		log_warnx("recv_hello: invalid hello-interval %d, "
142 		    "interface %s", ntohs(hello.hello_interval),
143 		    iface->name);
144 		return;
145 	}
146 
147 	if (ntohs(hello.rtr_dead_interval) != iface->dead_interval) {
148 		log_warnx("recv_hello: invalid router-dead-interval %d, "
149 		    "interface %s", ntohl(hello.rtr_dead_interval),
150 		    iface->name);
151 		return;
152 	}
153 
154 	if ((area = area_find(oeconf, iface->area_id)) == NULL)
155 		fatalx("interface lost area");
156 
157 	if ((hello.opts3 & OSPF_OPTION_E && area->stub) ||		/* XXX */
158 	    ((hello.opts3 & OSPF_OPTION_E) == 0 && !area->stub)) {	/* XXX */
159 		log_warnx("recv_hello: ExternalRoutingCapability mismatch, "
160 		    "interface %s", iface->name);
161 		return;
162 	}
163 
164 	switch (iface->type) {
165 	case IF_TYPE_POINTOPOINT:
166 	case IF_TYPE_VIRTUALLINK:
167 		/* match router-id */
168 		LIST_FOREACH(nbr, &iface->nbr_list, entry) {
169 			if (nbr == iface->self)
170 				continue;
171 			if (nbr->id.s_addr == rtr_id)
172 				break;
173 		}
174 		break;
175 	case IF_TYPE_BROADCAST:
176 	case IF_TYPE_NBMA:
177 	case IF_TYPE_POINTOMULTIPOINT:
178 		/* match src IP */
179 		LIST_FOREACH(nbr, &iface->nbr_list, entry) {
180 			if (nbr == iface->self)
181 				continue;
182 			if (IN6_ARE_ADDR_EQUAL(&nbr->addr, src))
183 				break;
184 		}
185 		break;
186 	default:
187 		fatalx("recv_hello: unknown interface type");
188 	}
189 
190 	if (!nbr) {
191 		nbr = nbr_new(rtr_id, iface, 0);
192 		/* set neighbor parameters */
193 		nbr->dr.s_addr = hello.d_rtr;
194 		nbr->bdr.s_addr = hello.bd_rtr;
195 		nbr->priority = hello.rtr_priority;
196 		nbr_change = 1;
197 	}
198 
199 	/* actually the neighbor address shouldn't be stored on virtual links */
200 	nbr->addr = *src;
201 	nbr->options = (hello.opts1 << 16) | (hello.opts2 << 8) | hello.opts3;
202 
203 	nbr_fsm(nbr, NBR_EVT_HELLO_RCVD);
204 
205 	while (len >= sizeof(nbr_id)) {
206 		memcpy(&nbr_id, buf, sizeof(nbr_id));
207 		if (nbr_id == ospfe_router_id()) {
208 			/* seen myself */
209 			if (nbr->state & NBR_STA_PRELIM)
210 				nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD);
211 			break;
212 		}
213 		buf += sizeof(nbr_id);
214 		len -= sizeof(nbr_id);
215 	}
216 
217 	if (len == 0) {
218 		nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD);
219 		/* set neighbor parameters */
220 		nbr->dr.s_addr = hello.d_rtr;
221 		nbr->bdr.s_addr = hello.bd_rtr;
222 		nbr->priority = hello.rtr_priority;
223 		return;
224 	}
225 
226 	if (nbr->priority != hello.rtr_priority) {
227 		nbr->priority = hello.rtr_priority;
228 		nbr_change = 1;
229 	}
230 
231 	if (iface->state & IF_STA_WAITING &&
232 	    hello.d_rtr == nbr->id.s_addr && hello.bd_rtr == 0)
233 		if_fsm(iface, IF_EVT_BACKUP_SEEN);
234 
235 	if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->id.s_addr) {
236 		/*
237 		 * In case we see the BDR make sure that the DR is around
238 		 * with a bidirectional (2_WAY or better) connection
239 		 */
240 		LIST_FOREACH(dr, &iface->nbr_list, entry)
241 			if (hello.d_rtr == dr->id.s_addr &&
242 			    dr->state & NBR_STA_BIDIR)
243 				if_fsm(iface, IF_EVT_BACKUP_SEEN);
244 	}
245 
246 	if ((nbr->id.s_addr == nbr->dr.s_addr &&
247 	    nbr->id.s_addr != hello.d_rtr) ||
248 	    (nbr->id.s_addr != nbr->dr.s_addr &&
249 	    nbr->id.s_addr == hello.d_rtr))
250 		/* neighbor changed from or to DR */
251 		nbr_change = 1;
252 	if ((nbr->id.s_addr == nbr->bdr.s_addr &&
253 	    nbr->id.s_addr != hello.bd_rtr) ||
254 	    (nbr->id.s_addr != nbr->bdr.s_addr &&
255 	    nbr->id.s_addr == hello.bd_rtr))
256 		/* neighbor changed from or to BDR */
257 		nbr_change = 1;
258 
259 	nbr->dr.s_addr = hello.d_rtr;
260 	nbr->bdr.s_addr = hello.bd_rtr;
261 
262 	if (nbr_change)
263 		if_fsm(iface, IF_EVT_NBR_CHNG);
264 
265 	/* TODO NBMA needs some special handling */
266 }
267