xref: /netbsd-src/usr.sbin/ldpd/ldp_peer.c (revision daf6c4152fcddc27c445489775ed1f66ab4ea9a9)
1 /* $NetBSD: ldp_peer.c,v 1.3 2010/12/30 11:29:21 kefren Exp $ */
2 
3 /*-
4  * Copyright (c) 2010 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Mihai Chelaru <kefren@NetBSD.org>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <netinet/tcp.h>
36 #include <netmpls/mpls.h>
37 #include <arpa/inet.h>
38 
39 #include <stdlib.h>
40 #include <strings.h>
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <errno.h>
44 
45 #include "conffile.h"
46 #include "socketops.h"
47 #include "ldp_errors.h"
48 #include "ldp.h"
49 #include "tlv_stack.h"
50 #include "mpls_interface.h"
51 #include "notifications.h"
52 #include "ldp_peer.h"
53 
54 extern int ldp_holddown_time;
55 
56 struct in_addr *myaddresses;
57 
58 void
59 ldp_peer_init(void)
60 {
61 	SLIST_INIT(&ldp_peer_head);
62 	myaddresses = NULL;
63 }
64 
65 /*
66  * soc should be > 1 if there is already a TCP socket for this else we'll
67  * initiate a new one
68  */
69 struct ldp_peer *
70 ldp_peer_new(struct in_addr * ldp_id, struct in_addr * a,
71 	     struct in_addr * tradd, uint16_t holdtime, int soc)
72 {
73 	struct ldp_peer *p;
74 	int s = soc;
75 	struct sockaddr_in sa;
76 	struct conf_neighbour *cn;
77 
78 	if (s < 1) {
79 		s = socket(PF_INET, SOCK_STREAM, 0);
80 		memset(&sa, 0, sizeof(sa));
81 		sa.sin_len = sizeof(sa);
82 		sa.sin_family = AF_INET;
83 
84 		if (tradd)
85 			memcpy(&sa.sin_addr, tradd,
86 			    sizeof(struct in_addr));
87 		else
88 			memcpy(&sa.sin_addr, a,
89 			    sizeof(struct in_addr));
90 		sa.sin_port = htons(LDP_PORT);
91 
92 		set_ttl(s);
93 	}
94 
95 	/* MD5 authentication needed ? */
96 	SLIST_FOREACH(cn, &conei_head, neilist)
97 		if (cn->authenticate != 0 && (a->s_addr == cn->address.s_addr ||
98 		    (tradd && tradd->s_addr == cn->address.s_addr))) {
99 			if (setsockopt(s, IPPROTO_TCP, TCP_MD5SIG, &(int){1},
100 			    sizeof(int)) != 0)
101 				fatalp("setsockopt TCP_MD5SIG: %s\n",
102 				    strerror(errno));
103 			break;
104 		}
105 
106 	/* Set the peer in CONNECTING/CONNECTED state */
107 	p = calloc(1, sizeof(*p));
108 
109 	if (!p) {
110 		fatalp("ldp_peer_new: calloc problem\n");
111 		return NULL;
112 	}
113 
114 	SLIST_INSERT_HEAD(&ldp_peer_head, p, peers);
115 	memcpy(&p->address, a, sizeof(struct in_addr));
116 	memcpy(&p->ldp_id, ldp_id, sizeof(struct in_addr));
117 	if (tradd)
118 		memcpy(&p->transport_address, tradd,
119 		    sizeof(struct in_addr));
120 	else
121 		memcpy(&p->transport_address, a,
122 		    sizeof(struct in_addr));
123 	p->holdtime = holdtime > ldp_holddown_time ? holdtime : ldp_holddown_time;
124 	p->socket = s;
125 	if (soc < 1) {
126 		p->state = LDP_PEER_CONNECTING;
127 		p->master = 1;
128 	} else {
129 		p->state = LDP_PEER_CONNECTED;
130 		p->master = 0;
131 		set_ttl(p->socket);
132 	}
133 	SLIST_INIT(&p->ldp_peer_address_head);
134 	SLIST_INIT(&p->label_mapping_head);
135 	p->timeout = p->holdtime;
136 
137 	/* And connect to peer */
138 	if (soc < 1)
139 		if (connect(s, (struct sockaddr *) & sa, sizeof(sa)) == -1) {
140 			if (errno == EINTR) {
141 				return p;	/* We take care of this in
142 						 * big_loop */
143 			}
144 			warnp("connect to %s failed: %s\n",
145 			    inet_ntoa(sa.sin_addr), strerror(errno));
146 			ldp_peer_holddown(p);
147 			return NULL;
148 		}
149 	p->state = LDP_PEER_CONNECTED;
150 	return p;
151 }
152 
153 void
154 ldp_peer_holddown(struct ldp_peer * p)
155 {
156 	if (!p)
157 		return;
158 	if (p->state == LDP_PEER_ESTABLISHED)
159 		mpls_delete_ldp_peer(p);
160 	p->state = LDP_PEER_HOLDDOWN;
161 	p->timeout = ldp_holddown_time;
162 	shutdown(p->socket, SHUT_RDWR);
163 	ldp_peer_delete_all_mappings(p);
164 	del_all_ifaddr(p);
165 	fatalp("LDP Neighbour %s is DOWN\n", inet_ntoa(p->ldp_id));
166 }
167 
168 void
169 ldp_peer_holddown_all()
170 {
171 	struct ldp_peer *p;
172 
173 	SLIST_FOREACH(p, &ldp_peer_head, peers) {
174 		if ((p->state == LDP_PEER_ESTABLISHED) ||
175 		    (p->state == LDP_PEER_CONNECTED))
176 			send_notification(p, get_message_id(), NOTIF_SHUTDOWN);
177 		ldp_peer_holddown(p);
178 	}
179 }
180 
181 void
182 ldp_peer_delete(struct ldp_peer * p)
183 {
184 
185 	if (!p)
186 		return;
187 
188 	SLIST_REMOVE(&ldp_peer_head, p, ldp_peer, peers);
189 	close(p->socket);
190 	warnp("LDP Neighbor %s holddown timer expired\n", inet_ntoa(p->ldp_id));
191 	free(p);
192 }
193 
194 struct ldp_peer *
195 get_ldp_peer(struct in_addr * a)
196 {
197 	struct ldp_peer *p;
198 
199 	SLIST_FOREACH(p, &ldp_peer_head, peers) {
200 		if (!memcmp((void *) a, (void *) &p->ldp_id,
201 		    sizeof(struct in_addr)))
202 			return p;
203 		if (!memcmp((void *) a, (void *) &p->address,
204 		    sizeof(struct in_addr)))
205 			return p;
206 		if (check_ifaddr(p, a))
207 			return p;
208 	}
209 	return NULL;
210 }
211 
212 struct ldp_peer *
213 get_ldp_peer_by_socket(int s)
214 {
215 	struct ldp_peer *p;
216 
217 	SLIST_FOREACH(p, &ldp_peer_head, peers)
218 		if (p->socket == s)
219 			return p;
220 	return NULL;
221 }
222 
223 /*
224  * Adds address list bounded to a specific peer
225  * Returns the number of addresses inserted successfuly
226  */
227 int
228 add_ifaddresses(struct ldp_peer * p, struct al_tlv * a)
229 {
230 	int             i, c, n;
231 	struct in_addr *ia;
232 
233 	/*
234 	 * Check if tlv is Address type, if it's correct size (at least one
235 	 * address) and if it's IPv4
236 	 */
237 
238 	if ((ntohs(a->type) != TLV_ADDRESS_LIST) ||
239 	    (ntohs(a->length) < sizeof(a->af) + sizeof(struct in_addr)) ||
240 	    (ntohs(a->af) != LDP_AF_INET))
241 		return 0;
242 
243 	/* Number of addresses to insert */
244 	n = (ntohs(a->length) - sizeof(a->af)) / sizeof(struct in_addr);
245 
246 	debugp("Trying to add %d addresses to peer %s ... \n", n,
247 	    inet_ntoa(p->ldp_id));
248 
249 	for (ia = (struct in_addr *) & a->address, c = 0, i = 0; i < n; i++) {
250 		if (add_ifaddr(p, &ia[i]) == LDP_E_OK)
251 			c++;
252 	}
253 
254 	debugp("Added %d addresses\n", c);
255 
256 	return c;
257 }
258 
259 int
260 del_ifaddresses(struct ldp_peer * p, struct al_tlv * a)
261 {
262 	int             i, c, n;
263 	struct in_addr *ia;
264 
265 	/*
266 	 * Check if tlv is Address type, if it's correct size (at least one
267 	 * address) and if it's IPv4
268 	 */
269 
270 	if (ntohs(a->type) != TLV_ADDRESS_LIST ||
271 	    ntohs(a->length) > sizeof(a->af) + sizeof(struct in_addr) ||
272 	    ntohs(a->af) != LDP_AF_INET)
273 		return -1;
274 
275 	n = (ntohs(a->length) - sizeof(a->af)) / sizeof(struct in_addr);
276 
277 	debugp("Trying to delete %d addresses from peer %s ... \n", n,
278 	    inet_ntoa(p->ldp_id));
279 
280 	for (ia = (struct in_addr *) & a[1], c = 0, i = 0; i < n; i++) {
281 		if (del_ifaddr(p, &ia[i]) == LDP_E_OK)
282 			c++;
283 	}
284 
285 	debugp("Deleted %d addresses\n", c);
286 
287 	return c;
288 }
289 
290 
291 /* Adds a _SINGLE_ address to a specific peer */
292 int
293 add_ifaddr(struct ldp_peer * p, struct in_addr * a)
294 {
295 	struct ldp_peer_address *lpa;
296 
297 	/* Is it already there ? */
298 	if (check_ifaddr(p, a))
299 		return LDP_E_ALREADY_DONE;
300 
301 	lpa = calloc(1, sizeof(*lpa));
302 
303 	if (!lpa) {
304 		fatalp("add_ifaddr: malloc problem\n");
305 		return LDP_E_MEMORY;
306 	}
307 
308 	memcpy(&lpa->address, a, sizeof(struct in_addr));
309 
310 	SLIST_INSERT_HEAD(&p->ldp_peer_address_head, lpa, addresses);
311 	return LDP_E_OK;
312 }
313 
314 /* Deletes an address bounded to a specific peer */
315 int
316 del_ifaddr(struct ldp_peer * p, struct in_addr * a)
317 {
318 	struct ldp_peer_address *wp;
319 
320 	wp = check_ifaddr(p, a);
321 	if (!wp)
322 		return LDP_E_NOENT;
323 
324 	SLIST_REMOVE(&p->ldp_peer_address_head, wp, ldp_peer_address,
325 	    addresses);
326 	free(wp);
327 	return LDP_E_OK;
328 }
329 
330 /* Checks if an address is already bounded */
331 struct ldp_peer_address *
332 check_ifaddr(struct ldp_peer * p, struct in_addr * a)
333 {
334 	struct ldp_peer_address *wp;
335 
336 	SLIST_FOREACH(wp, &p->ldp_peer_address_head, addresses)
337 		if (memcmp(a, &wp->address, sizeof(struct in_addr)) == 0)
338 			return wp;
339 	return NULL;
340 }
341 
342 void
343 del_all_ifaddr(struct ldp_peer * p)
344 {
345 	struct ldp_peer_address *wp;
346 
347 	while (!SLIST_EMPTY(&p->ldp_peer_address_head)) {
348 		wp = SLIST_FIRST(&p->ldp_peer_address_head);
349 		SLIST_REMOVE_HEAD(&p->ldp_peer_address_head, addresses);
350 		free(wp);
351 	}
352 }
353 
354 void
355 print_bounded_addresses(struct ldp_peer * p)
356 {
357 	struct ldp_peer_address *wp;
358 	char abuf[512];
359 
360 	snprintf(abuf, sizeof(abuf), "Addresses bounded to peer %s: ",
361 		inet_ntoa(p->address));
362 	SLIST_FOREACH(wp, &p->ldp_peer_address_head, addresses) {
363 		strncat(abuf, inet_ntoa(wp->address), sizeof(abuf) -1);
364 		strncat(abuf, " ", sizeof(abuf) -1);
365 	}
366 	warnp("%s\n", abuf);
367 }
368 
369 void
370 add_my_if_addrs(struct in_addr * a, int count)
371 {
372 	myaddresses = calloc((count + 1), sizeof(*myaddresses));
373 
374 	if (!myaddresses) {
375 		fatalp("add_my_if_addrs: malloc problem\n");
376 		return;
377 	}
378 	memcpy(myaddresses, a, count * sizeof(struct in_addr));
379 	myaddresses[count].s_addr = 0;
380 }
381 
382 /* Adds a label and a prefix to a specific peer */
383 int
384 ldp_peer_add_mapping(struct ldp_peer * p, struct in_addr * a, int prefix,
385     int label)
386 {
387 	struct label_mapping *lma;
388 
389 	if (!p)
390 		return -1;
391 	if (ldp_peer_get_lm(p, a, prefix))
392 		return LDP_E_ALREADY_DONE;
393 
394 	lma = malloc(sizeof(*lma));
395 
396 	if (!lma) {
397 		fatalp("ldp_peer_add_mapping: malloc problem\n");
398 		return LDP_E_MEMORY;
399 	}
400 
401 	memcpy(&lma->address, a, sizeof(struct in_addr));
402 	lma->prefix = prefix;
403 	lma->label = label;
404 
405 	SLIST_INSERT_HEAD(&p->label_mapping_head, lma, mappings);
406 
407 	return LDP_E_OK;
408 }
409 
410 int
411 ldp_peer_delete_mapping(struct ldp_peer * p, struct in_addr * a, int prefix)
412 {
413 	struct label_mapping *lma;
414 
415 	if (!a)
416 		return ldp_peer_delete_all_mappings(p);
417 
418 	lma = ldp_peer_get_lm(p, a, prefix);
419 	if (!lma)
420 		return LDP_E_NOENT;
421 
422 	SLIST_REMOVE(&p->label_mapping_head, lma, label_mapping, mappings);
423 	free(lma);
424 
425 	return LDP_E_OK;
426 }
427 
428 struct label_mapping *
429 ldp_peer_get_lm(struct ldp_peer * p, struct in_addr * a, int prefix)
430 {
431 	struct label_mapping *rv;
432 
433 	if (!p)
434 		return NULL;
435 
436 	SLIST_FOREACH(rv, &p->label_mapping_head, mappings)
437 		if ((rv->prefix == prefix) && (!memcmp(a, &rv->address,
438 		    sizeof(struct in_addr))))
439 			break;
440 
441 	return rv;
442 
443 }
444 
445 int
446 ldp_peer_delete_all_mappings(struct ldp_peer * p)
447 {
448 	struct label_mapping *lma;
449 
450 	while(!SLIST_EMPTY(&p->label_mapping_head)) {
451 		lma = SLIST_FIRST(&p->label_mapping_head);
452 		SLIST_REMOVE_HEAD(&p->label_mapping_head, mappings);
453 		free(lma);
454 	}
455 
456 	return LDP_E_OK;
457 }
458 
459 /* returns a mapping and its peer */
460 struct peer_map *
461 ldp_test_mapping(struct in_addr * a, int prefix, struct in_addr * gate)
462 {
463 	struct ldp_peer *lpeer;
464 	struct peer_map *rv = NULL;
465 	struct label_mapping *lm = NULL;
466 
467 	/* Checks if it's LPDID, else checks if it's an interface */
468 
469 	lpeer = get_ldp_peer(gate);
470 	if (!lpeer) {
471 		debugp("Gateway %s is not an LDP peer\n", inet_ntoa(*gate));
472 		return NULL;
473 	}
474 	if (lpeer->state != LDP_PEER_ESTABLISHED) {
475 		warnp("ldp_test_mapping: peer is down ?!\n");
476 		return NULL;
477 	}
478 	lm = ldp_peer_get_lm(lpeer, a, prefix);
479 
480 	if (!lm) {
481 		debugp("Cannot match that prefix to the specified peer\n");
482 		return NULL;
483 	}
484 	rv = malloc(sizeof(*rv));
485 
486 	if (!rv) {
487 		fatalp("ldp_test_mapping: malloc problem\n");
488 		return NULL;
489 	}
490 
491 	rv->lm = lm;
492 	rv->peer = lpeer;
493 
494 	return rv;
495 }
496 
497 /* Name from state */
498 const char * ldp_state_to_name(int state)
499 {
500 	switch(state) {
501 		case LDP_PEER_CONNECTING:
502 			return "CONNECTING";
503 		case LDP_PEER_CONNECTED:
504 			return "CONNECTED";
505 		case LDP_PEER_ESTABLISHED:
506 			return "ESTABLISHED";
507 		case LDP_PEER_HOLDDOWN:
508 			return "HOLDDOWN";
509 	}
510 	return "UNKNOWN";
511 }
512