xref: /openbsd-src/usr.sbin/ldpd/hello.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: hello.c,v 1.23 2013/10/15 20:31:13 renato Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/uio.h>
22 
23 #include <netinet/in.h>
24 #include <netinet/in_systm.h>
25 #include <netinet/ip.h>
26 #include <arpa/inet.h>
27 #include <net/if_dl.h>
28 
29 #include <errno.h>
30 #include <event.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "ldpd.h"
35 #include "ldp.h"
36 #include "log.h"
37 #include "ldpe.h"
38 
39 extern struct ldpd_conf        *leconf;
40 
41 int	tlv_decode_hello_prms(char *, u_int16_t, u_int16_t *, u_int16_t *);
42 int	tlv_decode_opt_hello_prms(char *, u_int16_t, struct in_addr *,
43 	    u_int32_t *);
44 int	gen_hello_prms_tlv(struct ibuf *buf, u_int16_t, u_int16_t);
45 int	gen_opt4_hello_prms_tlv(struct ibuf *, u_int16_t, u_int32_t);
46 
47 int
48 send_hello(enum hello_type type, struct iface *iface, struct tnbr *tnbr)
49 {
50 	struct sockaddr_in	 dst;
51 	struct ibuf		*buf;
52 	u_int16_t		 size, holdtime = 0, flags = 0;
53 	int			 fd = 0;
54 
55 	dst.sin_port = htons(LDP_PORT);
56 	dst.sin_family = AF_INET;
57 	dst.sin_len = sizeof(struct sockaddr_in);
58 
59 	switch (type) {
60 	case HELLO_LINK:
61 		inet_aton(AllRouters, &dst.sin_addr);
62 		holdtime = iface->hello_holdtime;
63 		flags = 0;
64 		fd = iface->discovery_fd;
65 		break;
66 	case HELLO_TARGETED:
67 		dst.sin_addr.s_addr = tnbr->addr.s_addr;
68 		holdtime = tnbr->hello_holdtime;
69 		flags = TARGETED_HELLO;
70 		if (tnbr->flags & F_TNBR_CONFIGURED)
71 			flags |= REQUEST_TARG_HELLO;
72 		fd = tnbr->discovery_fd;
73 		break;
74 	}
75 
76 	if ((buf = ibuf_open(LDP_MAX_LEN)) == NULL)
77 		fatal("send_hello");
78 
79 	size = LDP_HDR_SIZE + sizeof(struct ldp_msg) +
80 	    sizeof(struct hello_prms_tlv) +
81 	    sizeof(struct hello_prms_opt4_tlv);
82 
83 	gen_ldp_hdr(buf, size);
84 
85 	size -= LDP_HDR_SIZE;
86 
87 	gen_msg_tlv(buf, MSG_TYPE_HELLO, size);
88 
89 	gen_hello_prms_tlv(buf, holdtime, flags);
90 	gen_opt4_hello_prms_tlv(buf, TLV_TYPE_IPV4TRANSADDR, ldpe_router_id());
91 
92 	send_packet(fd, iface, buf->buf, buf->wpos, &dst);
93 	ibuf_free(buf);
94 
95 	return (0);
96 }
97 
98 void
99 recv_hello(struct iface *iface, struct in_addr src, char *buf, u_int16_t len)
100 {
101 	struct ldp_msg		 hello;
102 	struct ldp_hdr		 ldp;
103 	struct adj		*adj;
104 	struct nbr		*nbr;
105 	struct in_addr		 lsr_id;
106 	struct in_addr		 transport_addr;
107 	u_int32_t		 conf_number;
108 	u_int16_t		 holdtime, flags;
109 	int			 r;
110 	struct hello_source	 source;
111 	struct tnbr		*tnbr = NULL;
112 
113 	bcopy(buf, &ldp, sizeof(ldp));
114 	buf += LDP_HDR_SIZE;
115 	len -= LDP_HDR_SIZE;
116 
117 	bcopy(buf, &hello, sizeof(hello));
118 	buf += sizeof(struct ldp_msg);
119 	len -= sizeof(struct ldp_msg);
120 
121 	lsr_id.s_addr = ldp.lsr_id;
122 
123 	r = tlv_decode_hello_prms(buf, len, &holdtime, &flags);
124 	if (r == -1) {
125 		log_debug("recv_hello: neighbor %s: failed to decode params",
126 		    inet_ntoa(lsr_id));
127 		return;
128 	}
129 
130 	bzero(&source, sizeof(source));
131 	if (flags & TARGETED_HELLO) {
132 		tnbr = tnbr_find(src);
133 		if (!tnbr) {
134 			if (!((flags & REQUEST_TARG_HELLO) &&
135 			    leconf->flags & LDPD_FLAG_TH_ACCEPT))
136 				return;
137 
138 			tnbr = tnbr_new(leconf, src, 0);
139 			if (!tnbr)
140 				return;
141 			tnbr_init(leconf, tnbr);
142 			LIST_INSERT_HEAD(&leconf->tnbr_list, tnbr, entry);
143 		}
144 		source.type = HELLO_TARGETED;
145 		source.target = tnbr;
146 	} else {
147 		if (ldp.lspace_id != 0) {
148 			log_debug("recv_hello: invalid label space "
149 			    "ID %u, interface %s", ldp.lspace_id,
150 			    iface->name);
151 			return;
152 		}
153 		source.type = HELLO_LINK;
154 		source.link.iface = iface;
155 		source.link.src_addr.s_addr = src.s_addr;
156 	}
157 
158 	buf += r;
159 	len -= r;
160 
161 	r = tlv_decode_opt_hello_prms(buf, len, &transport_addr,
162 	    &conf_number);
163 	if (r == -1) {
164 		log_debug("recv_hello: neighbor %s: failed to decode "
165 		    "optional params", inet_ntoa(lsr_id));
166 		return;
167 	}
168 	if (transport_addr.s_addr == INADDR_ANY)
169 		transport_addr.s_addr = src.s_addr;
170 
171 	if (r != len) {
172 		log_debug("recv_hello: neighbor %s: unexpected data in message",
173 		    inet_ntoa(lsr_id));
174 		return;
175 	}
176 
177 	nbr = nbr_find_ldpid(ldp.lsr_id);
178 	if (!nbr) {
179 		/* create new adjacency and new neighbor */
180 		nbr = nbr_new(lsr_id, transport_addr);
181 		adj = adj_new(nbr, &source, holdtime, transport_addr);
182 	} else {
183 		adj = adj_find(nbr, &source);
184 		if (!adj) {
185 			/* create new adjacency for existing neighbor */
186 			adj = adj_new(nbr, &source, holdtime, transport_addr);
187 
188 			if (nbr->addr.s_addr != transport_addr.s_addr)
189 				log_warnx("recv_hello: neighbor %s: multiple "
190 				    "adjacencies advertising different "
191 				    "transport addresses", inet_ntoa(lsr_id));
192 		}
193 	}
194 
195 	/* always update the holdtime to properly handle runtime changes */
196 	switch (source.type) {
197 	case HELLO_LINK:
198 		if (holdtime == 0)
199 			holdtime = LINK_DFLT_HOLDTIME;
200 
201 		adj->holdtime = min(iface->hello_holdtime, holdtime);
202 		break;
203 	case HELLO_TARGETED:
204 		if (holdtime == 0)
205 			holdtime = TARGETED_DFLT_HOLDTIME;
206 
207 		adj->holdtime = min(tnbr->hello_holdtime, holdtime);
208 	}
209 
210 	if (adj->holdtime != INFINITE_HOLDTIME)
211 		adj_start_itimer(adj);
212 	else
213 		adj_stop_itimer(adj);
214 
215 	if (nbr->state == NBR_STA_PRESENT && nbr_session_active_role(nbr) &&
216 	    !nbr_pending_connect(nbr) && !nbr_pending_idtimer(nbr))
217 		nbr_establish_connection(nbr);
218 }
219 
220 int
221 gen_hello_prms_tlv(struct ibuf *buf, u_int16_t holdtime, u_int16_t flags)
222 {
223 	struct hello_prms_tlv	parms;
224 
225 	bzero(&parms, sizeof(parms));
226 	parms.type = htons(TLV_TYPE_COMMONHELLO);
227 	parms.length = htons(sizeof(parms.holdtime) + sizeof(parms.flags));
228 	parms.holdtime = htons(holdtime);
229 	parms.flags = htons(flags);
230 
231 	return (ibuf_add(buf, &parms, sizeof(parms)));
232 }
233 
234 int
235 gen_opt4_hello_prms_tlv(struct ibuf *buf, u_int16_t type, u_int32_t value)
236 {
237 	struct hello_prms_opt4_tlv	parms;
238 
239 	bzero(&parms, sizeof(parms));
240 	parms.type = htons(type);
241 	parms.length = htons(4);
242 	parms.value = value;
243 
244 	return (ibuf_add(buf, &parms, sizeof(parms)));
245 }
246 
247 int
248 tlv_decode_hello_prms(char *buf, u_int16_t len, u_int16_t *holdtime,
249     u_int16_t *flags)
250 {
251 	struct hello_prms_tlv	tlv;
252 
253 	if (len < sizeof(tlv))
254 		return (-1);
255 	bcopy(buf, &tlv, sizeof(tlv));
256 
257 	if (ntohs(tlv.length) != sizeof(tlv) - TLV_HDR_LEN)
258 		return (-1);
259 
260 	if (tlv.type != htons(TLV_TYPE_COMMONHELLO))
261 		return (-1);
262 
263 	*holdtime = ntohs(tlv.holdtime);
264 	*flags = ntohs(tlv.flags);
265 
266 	return (sizeof(tlv));
267 }
268 
269 int
270 tlv_decode_opt_hello_prms(char *buf, u_int16_t len, struct in_addr *addr,
271     u_int32_t *conf_number)
272 {
273 	struct tlv	tlv;
274 	int		cons = 0;
275 	u_int16_t	tlv_len;
276 
277 	bzero(addr, sizeof(*addr));
278 	*conf_number = 0;
279 
280 	while (len >= sizeof(tlv)) {
281 		bcopy(buf, &tlv, sizeof(tlv));
282 		tlv_len = ntohs(tlv.length);
283 		switch (ntohs(tlv.type)) {
284 		case TLV_TYPE_IPV4TRANSADDR:
285 			if (tlv_len != sizeof(u_int32_t))
286 				return (-1);
287 			bcopy(buf + TLV_HDR_LEN, addr, sizeof(u_int32_t));
288 			break;
289 		case TLV_TYPE_CONFIG:
290 			if (tlv_len != sizeof(u_int32_t))
291 				return (-1);
292 			bcopy(buf + TLV_HDR_LEN, conf_number,
293 			    sizeof(u_int32_t));
294 			break;
295 		default:
296 			/* if unknown flag set, ignore TLV */
297 			if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
298 				return (-1);
299 			break;
300 		}
301 		buf += TLV_HDR_LEN + tlv_len;
302 		len -= TLV_HDR_LEN + tlv_len;
303 		cons += TLV_HDR_LEN + tlv_len;
304 	}
305 
306 	return (cons);
307 }
308