xref: /openbsd/usr.sbin/ospf6d/hello.c (revision 898184e3)
1 /*	$OpenBSD: hello.c,v 1.16 2010/05/26 13:56:08 nicm 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 ibuf		*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 IBUF_READ_SIZE */
66 	if ((buf = ibuf_dynamic(PKG_DEF_SIZE, IBUF_READ_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 = htonl(iface->ifindex);
75 	LSA_24_SETHI(hello.opts, iface->priority);
76 	opts = area_ospf_options(area_find(oeconf, iface->area_id));
77 	LSA_24_SETLO(hello.opts, opts);
78 	hello.opts = htonl(hello.opts);
79 
80 	hello.hello_interval = htons(iface->hello_interval);
81 	hello.rtr_dead_interval = htons(iface->dead_interval);
82 
83 	if (iface->dr) {
84 		hello.d_rtr = iface->dr->id.s_addr;
85 		iface->self->dr.s_addr = iface->dr->id.s_addr;
86 	} else
87 		hello.d_rtr = 0;
88 	if (iface->bdr) {
89 		hello.bd_rtr = iface->bdr->id.s_addr;
90 		iface->self->bdr.s_addr = iface->bdr->id.s_addr;
91 	} else
92 		hello.bd_rtr = 0;
93 
94 	if (ibuf_add(buf, &hello, sizeof(hello)))
95 		goto fail;
96 
97 	/* active neighbor(s) */
98 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
99 		if ((nbr->state >= NBR_STA_INIT) && (nbr != iface->self))
100 			if (ibuf_add(buf, &nbr->id, sizeof(nbr->id)))
101 				goto fail;
102 	}
103 
104 	/* calculate checksum */
105 	if (upd_ospf_hdr(buf, iface))
106 		goto fail;
107 
108 	ret = send_packet(iface, buf->buf, buf->wpos, &dst);
109 
110 	ibuf_free(buf);
111 	return (ret);
112 fail:
113 	log_warn("send_hello");
114 	ibuf_free(buf);
115 	return (-1);
116 }
117 
118 void
119 recv_hello(struct iface *iface, struct in6_addr *src, u_int32_t rtr_id,
120     char *buf, u_int16_t len)
121 {
122 	struct hello_hdr	 hello;
123 	struct nbr		*nbr = NULL, *dr;
124 	struct area		*area;
125 	u_int32_t		 nbr_id, opts;
126 	int			 nbr_change = 0;
127 
128 	if (len < sizeof(hello) || (len & 0x03)) {
129 		log_warnx("recv_hello: bad packet size, interface %s",
130 		    iface->name);
131 		return;
132 	}
133 
134 	memcpy(&hello, buf, sizeof(hello));
135 	buf += sizeof(hello);
136 	len -= sizeof(hello);
137 
138 	if (ntohs(hello.hello_interval) != iface->hello_interval) {
139 		log_warnx("recv_hello: invalid hello-interval %d, "
140 		    "interface %s", ntohs(hello.hello_interval),
141 		    iface->name);
142 		return;
143 	}
144 
145 	if (ntohs(hello.rtr_dead_interval) != iface->dead_interval) {
146 		log_warnx("recv_hello: invalid router-dead-interval %d, "
147 		    "interface %s", ntohl(hello.rtr_dead_interval),
148 		    iface->name);
149 		return;
150 	}
151 
152 	if ((area = area_find(oeconf, iface->area_id)) == NULL)
153 		fatalx("interface lost area");
154 
155 	opts = LSA_24_GETLO(ntohl(hello.opts));
156 	if ((opts & OSPF_OPTION_E && area->stub) ||
157 	    ((opts & OSPF_OPTION_E) == 0 && !area->stub)) {
158 		log_warnx("recv_hello: ExternalRoutingCapability mismatch, "
159 		    "interface %s", iface->name);
160 		return;
161 	}
162 
163 	/* match router-id */
164 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
165 		if (nbr == iface->self)
166 			continue;
167 		if (nbr->id.s_addr == rtr_id)
168 			break;
169 	}
170 
171 	if (!nbr) {
172 		nbr = nbr_new(rtr_id, iface, ntohl(hello.iface_id), 0, src);
173 		/* set neighbor parameters */
174 		nbr->dr.s_addr = hello.d_rtr;
175 		nbr->bdr.s_addr = hello.bd_rtr;
176 		nbr->priority = LSA_24_GETHI(ntohl(hello.opts));
177 		nbr_change = 1;
178 	}
179 
180 	/* actually the neighbor address shouldn't be stored on virtual links */
181 	nbr->addr = *src;
182 	nbr->options = opts;
183 
184 	nbr_fsm(nbr, NBR_EVT_HELLO_RCVD);
185 
186 	while (len >= sizeof(nbr_id)) {
187 		memcpy(&nbr_id, buf, sizeof(nbr_id));
188 		if (nbr_id == ospfe_router_id()) {
189 			/* seen myself */
190 			if (nbr->state & NBR_STA_PRELIM)
191 				nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD);
192 			break;
193 		}
194 		buf += sizeof(nbr_id);
195 		len -= sizeof(nbr_id);
196 	}
197 
198 	if (len == 0) {
199 		nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD);
200 		/* set neighbor parameters */
201 		nbr->dr.s_addr = hello.d_rtr;
202 		nbr->bdr.s_addr = hello.bd_rtr;
203 		nbr->priority = LSA_24_GETHI(ntohl(hello.opts));
204 		return;
205 	}
206 
207 	if (nbr->priority != LSA_24_GETHI(ntohl(hello.opts))) {
208 		nbr->priority = LSA_24_GETHI(ntohl(hello.opts));
209 		nbr_change = 1;
210 	}
211 
212 	if (iface->state & IF_STA_WAITING &&
213 	    hello.d_rtr == nbr->id.s_addr && hello.bd_rtr == 0)
214 		if_fsm(iface, IF_EVT_BACKUP_SEEN);
215 
216 	if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->id.s_addr) {
217 		/*
218 		 * In case we see the BDR make sure that the DR is around
219 		 * with a bidirectional (2_WAY or better) connection
220 		 */
221 		LIST_FOREACH(dr, &iface->nbr_list, entry)
222 			if (hello.d_rtr == dr->id.s_addr &&
223 			    dr->state & NBR_STA_BIDIR)
224 				if_fsm(iface, IF_EVT_BACKUP_SEEN);
225 	}
226 
227 	if ((nbr->id.s_addr == nbr->dr.s_addr &&
228 	    nbr->id.s_addr != hello.d_rtr) ||
229 	    (nbr->id.s_addr != nbr->dr.s_addr &&
230 	    nbr->id.s_addr == hello.d_rtr))
231 		/* neighbor changed from or to DR */
232 		nbr_change = 1;
233 	if ((nbr->id.s_addr == nbr->bdr.s_addr &&
234 	    nbr->id.s_addr != hello.bd_rtr) ||
235 	    (nbr->id.s_addr != nbr->bdr.s_addr &&
236 	    nbr->id.s_addr == hello.bd_rtr))
237 		/* neighbor changed from or to BDR */
238 		nbr_change = 1;
239 
240 	nbr->dr.s_addr = hello.d_rtr;
241 	nbr->bdr.s_addr = hello.bd_rtr;
242 
243 	if (nbr_change)
244 		if_fsm(iface, IF_EVT_NBR_CHNG);
245 
246 	/* TODO NBMA needs some special handling */
247 }
248