ldp_peer.c revision 1.14 1 /* $NetBSD: ldp_peer.c,v 1.14 2013/07/18 06:07:45 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 (at) 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 <assert.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <strings.h>
43 #include <stdio.h>
44 #include <unistd.h>
45
46 #include "conffile.h"
47 #include "socketops.h"
48 #include "ldp_errors.h"
49 #include "ldp.h"
50 #include "tlv_stack.h"
51 #include "mpls_interface.h"
52 #include "notifications.h"
53 #include "ldp_peer.h"
54
55 extern int ldp_holddown_time;
56
57 void
58 ldp_peer_init(void)
59 {
60 SLIST_INIT(&ldp_peer_head);
61 }
62
63 int
64 sockaddr_cmp(const struct sockaddr *a, const struct sockaddr *b)
65 {
66 if (a == NULL || b == NULL || a->sa_len != b->sa_len ||
67 a->sa_family != b->sa_family)
68 return -1;
69 return memcmp(a, b, a->sa_len);
70 }
71 /*
72 * soc should be > 1 if there is already a TCP socket for this else we'll
73 * initiate a new one
74 */
75 struct ldp_peer *
76 ldp_peer_new(const struct in_addr * ldp_id, const struct sockaddr * padd,
77 const struct sockaddr * tradd, uint16_t holdtime, int soc)
78 {
79 struct ldp_peer *p;
80 int s = soc;
81 struct sockaddr *connecting_sa = NULL;
82 struct conf_neighbour *cn;
83
84 if (tradd != NULL)
85 assert(tradd->sa_family == padd->sa_family);
86
87 if (soc < 1) {
88 s = socket(PF_INET, SOCK_STREAM, 0);
89 if (s < 0) {
90 fatalp("ldp_peer_new: cannot create socket\n");
91 return NULL;
92 }
93 if (tradd != NULL) {
94 connecting_sa = malloc(tradd->sa_len);
95 memcpy(connecting_sa, tradd, tradd->sa_len);
96 } else {
97 connecting_sa = malloc(padd->sa_len);
98 memcpy(connecting_sa, padd, padd->sa_len);
99 }
100
101 assert(connecting_sa->sa_family == AF_INET ||
102 connecting_sa->sa_family == AF_INET6);
103
104 if (connecting_sa->sa_family == AF_INET)
105 ((struct sockaddr_in*)connecting_sa)->sin_port =
106 htons(LDP_PORT);
107 else
108 ((struct sockaddr_in6*)connecting_sa)->sin6_port =
109 htons(LDP_PORT);
110
111 set_ttl(s);
112 }
113
114 /* MD5 authentication needed ? */
115 SLIST_FOREACH(cn, &conei_head, neilist)
116 if (cn->authenticate != 0 &&
117 ldp_id->s_addr == cn->address.s_addr) {
118 if (setsockopt(s, IPPROTO_TCP, TCP_MD5SIG, &(int){1},
119 sizeof(int)) != 0)
120 fatalp("setsockopt TCP_MD5SIG: %s\n",
121 strerror(errno));
122 break;
123 }
124
125 /* Set the peer in CONNECTING/CONNECTED state */
126 p = calloc(1, sizeof(*p));
127
128 if (!p) {
129 fatalp("ldp_peer_new: calloc problem\n");
130 return NULL;
131 }
132
133 SLIST_INSERT_HEAD(&ldp_peer_head, p, peers);
134 p->address = (struct sockaddr *)malloc(padd->sa_len);
135 memcpy(p->address, padd, padd->sa_len);
136 memcpy(&p->ldp_id, ldp_id, sizeof(struct in_addr));
137 if (tradd != NULL) {
138 p->transport_address = (struct sockaddr *)malloc(tradd->sa_len);
139 memcpy(p->transport_address, tradd, tradd->sa_len);
140 } else {
141 p->transport_address = (struct sockaddr *)malloc(padd->sa_len);
142 memcpy(p->transport_address, padd, padd->sa_len);
143 }
144 p->holdtime=holdtime > ldp_holddown_time ? holdtime : ldp_holddown_time;
145 p->socket = s;
146 if (soc < 1) {
147 p->state = LDP_PEER_CONNECTING;
148 p->master = 1;
149 } else {
150 p->state = LDP_PEER_CONNECTED;
151 p->master = 0;
152 set_ttl(p->socket);
153 }
154 SLIST_INIT(&p->ldp_peer_address_head);
155 SLIST_INIT(&p->label_mapping_head);
156 p->timeout = p->holdtime;
157
158 /* And connect to peer */
159 if (soc < 1)
160 if (connect(s, connecting_sa, connecting_sa->sa_len) == -1) {
161 if (errno == EINTR) {
162 free(connecting_sa);
163 return p; /* We take care of this in
164 * big_loop */
165 }
166 warnp("connect to %s failed: %s\n",
167 satos(connecting_sa), strerror(errno));
168 free(connecting_sa);
169 ldp_peer_holddown(p);
170 return NULL;
171 }
172 p->state = LDP_PEER_CONNECTED;
173 return p;
174 }
175
176 void
177 ldp_peer_holddown(struct ldp_peer * p)
178 {
179 if (!p)
180 return;
181 if (p->state == LDP_PEER_ESTABLISHED)
182 mpls_delete_ldp_peer(p);
183 p->state = LDP_PEER_HOLDDOWN;
184 p->timeout = ldp_holddown_time;
185 shutdown(p->socket, SHUT_RDWR);
186 ldp_peer_delete_all_mappings(p);
187 del_all_ifaddr(p);
188 fatalp("LDP Neighbour %s is DOWN\n", inet_ntoa(p->ldp_id));
189 }
190
191 void
192 ldp_peer_holddown_all()
193 {
194 struct ldp_peer *p;
195
196 SLIST_FOREACH(p, &ldp_peer_head, peers) {
197 if ((p->state == LDP_PEER_ESTABLISHED) ||
198 (p->state == LDP_PEER_CONNECTED))
199 send_notification(p, get_message_id(),
200 NOTIF_FATAL | NOTIF_SHUTDOWN);
201 ldp_peer_holddown(p);
202 }
203 }
204
205 void
206 ldp_peer_delete(struct ldp_peer * p)
207 {
208
209 if (!p)
210 return;
211
212 SLIST_REMOVE(&ldp_peer_head, p, ldp_peer, peers);
213 close(p->socket);
214 warnp("LDP Neighbor %s holddown timer expired\n", inet_ntoa(p->ldp_id));
215 free(p->address);
216 free(p->transport_address);
217 free(p);
218 }
219
220 struct ldp_peer *
221 get_ldp_peer(const struct sockaddr * a)
222 {
223 struct ldp_peer *p;
224 const struct sockaddr_in *a_inet = (const struct sockaddr_in *)a;
225
226 SLIST_FOREACH(p, &ldp_peer_head, peers) {
227 if (a->sa_family == AF_INET &&
228 memcmp((const void *) &a_inet->sin_addr,
229 (const void *) &p->ldp_id,
230 sizeof(struct in_addr)) == 0)
231 return p;
232 if (sockaddr_cmp(a, p->address) == 0 ||
233 sockaddr_cmp(a, p->transport_address) == 0 ||
234 check_ifaddr(p, a))
235 return p;
236 }
237 return NULL;
238 }
239
240 struct ldp_peer *
241 get_ldp_peer_by_id(const struct in_addr *a)
242 {
243 struct ldp_peer *p;
244
245 SLIST_FOREACH(p, &ldp_peer_head, peers)
246 if (memcmp((const void*)a,
247 (const void*)&p->ldp_id, sizeof(*a)) == 0)
248 return p;
249 return NULL;
250 }
251
252 struct ldp_peer *
253 get_ldp_peer_by_socket(int s)
254 {
255 struct ldp_peer *p;
256
257 SLIST_FOREACH(p, &ldp_peer_head, peers)
258 if (p->socket == s)
259 return p;
260 return NULL;
261 }
262
263 /*
264 * Adds address list bounded to a specific peer
265 * Returns the number of addresses inserted successfuly
266 */
267 int
268 add_ifaddresses(struct ldp_peer * p, const struct al_tlv * a)
269 {
270 int i, c, n;
271 const struct in_addr *ia;
272 struct sockaddr_in ipa;
273
274 memset(&ipa, 0, sizeof(ipa));
275 ipa.sin_len = sizeof(ipa);
276 ipa.sin_family = AF_INET;
277 /*
278 * Check if tlv is Address type, if it's correct size (at least one
279 * address) and if it's IPv4
280 */
281
282 if ((ntohs(a->type) != TLV_ADDRESS_LIST) ||
283 (ntohs(a->length) < sizeof(a->af) + sizeof(struct in_addr)) ||
284 (ntohs(a->af) != LDP_AF_INET))
285 return 0;
286
287 /* Number of addresses to insert */
288 n = (ntohs(a->length) - sizeof(a->af)) / sizeof(struct in_addr);
289
290 debugp("Trying to add %d addresses to peer %s ... \n", n,
291 inet_ntoa(p->ldp_id));
292
293 for (ia = (const struct in_addr *) & a->address,c = 0,i = 0; i<n; i++) {
294 memcpy(&ipa.sin_addr, &ia[i], sizeof(ipa.sin_addr));
295 if (add_ifaddr(p, (struct sockaddr *)&ipa) == LDP_E_OK)
296 c++;
297 }
298
299 debugp("Added %d addresses\n", c);
300
301 return c;
302 }
303
304 int
305 del_ifaddresses(struct ldp_peer * p, const struct al_tlv * a)
306 {
307 int i, c, n;
308 const struct in_addr *ia;
309 struct sockaddr_in ipa;
310
311 memset(&ipa, 0, sizeof(ipa));
312 ipa.sin_len = sizeof(ipa);
313 ipa.sin_family = AF_INET;
314 /*
315 * Check if tlv is Address type, if it's correct size (at least one
316 * address) and if it's IPv4
317 */
318
319 if (ntohs(a->type) != TLV_ADDRESS_LIST ||
320 ntohs(a->length) > sizeof(a->af) + sizeof(struct in_addr) ||
321 ntohs(a->af) != LDP_AF_INET)
322 return -1;
323
324 n = (ntohs(a->length) - sizeof(a->af)) / sizeof(struct in_addr);
325
326 debugp("Trying to delete %d addresses from peer %s ... \n", n,
327 inet_ntoa(p->ldp_id));
328
329 for (ia = (const struct in_addr *) & a[1], c = 0, i = 0; i < n; i++) {
330 memcpy(&ipa.sin_addr, &ia[i], sizeof(ipa.sin_addr));
331 if (del_ifaddr(p, (struct sockaddr *)&ipa) == LDP_E_OK)
332 c++;
333 }
334
335 debugp("Deleted %d addresses\n", c);
336
337 return c;
338 }
339
340
341 /* Adds a _SINGLE_ INET address to a specific peer */
342 int
343 add_ifaddr(struct ldp_peer * p, const struct sockaddr * a)
344 {
345 struct ldp_peer_address *lpa;
346
347 /* Is it already there ? */
348 if (check_ifaddr(p, a))
349 return LDP_E_ALREADY_DONE;
350
351 lpa = calloc(1, sizeof(*lpa));
352
353 if (!lpa) {
354 fatalp("add_ifaddr: malloc problem\n");
355 return LDP_E_MEMORY;
356 }
357
358 assert(a->sa_len <= sizeof(union sockunion));
359
360 memcpy(&lpa->address.sa, a, a->sa_len);
361
362 SLIST_INSERT_HEAD(&p->ldp_peer_address_head, lpa, addresses);
363 return LDP_E_OK;
364 }
365
366 /* Deletes an address bounded to a specific peer */
367 int
368 del_ifaddr(struct ldp_peer * p, const struct sockaddr * a)
369 {
370 struct ldp_peer_address *wp;
371
372 wp = check_ifaddr(p, a);
373 if (!wp)
374 return LDP_E_NOENT;
375
376 SLIST_REMOVE(&p->ldp_peer_address_head, wp, ldp_peer_address,
377 addresses);
378 free(wp);
379 return LDP_E_OK;
380 }
381
382 /* Checks if an address is already bounded */
383 struct ldp_peer_address *
384 check_ifaddr(const struct ldp_peer * p, const struct sockaddr * a)
385 {
386 struct ldp_peer_address *wp;
387
388 SLIST_FOREACH(wp, &p->ldp_peer_address_head, addresses)
389 if (sockaddr_cmp(a, &wp->address.sa) == 0)
390 return wp;
391 return NULL;
392 }
393
394 void
395 del_all_ifaddr(struct ldp_peer * p)
396 {
397 struct ldp_peer_address *wp;
398
399 while (!SLIST_EMPTY(&p->ldp_peer_address_head)) {
400 wp = SLIST_FIRST(&p->ldp_peer_address_head);
401 SLIST_REMOVE_HEAD(&p->ldp_peer_address_head, addresses);
402 free(wp);
403 }
404 }
405
406 void
407 print_bounded_addresses(const struct ldp_peer * p)
408 {
409 struct ldp_peer_address *wp;
410 char abuf[512];
411
412 snprintf(abuf, sizeof(abuf), "Addresses bounded to peer %s: ",
413 satos(p->address));
414 SLIST_FOREACH(wp, &p->ldp_peer_address_head, addresses) {
415 strncat(abuf, satos(&wp->address.sa),
416 sizeof(abuf) -1);
417 strncat(abuf, " ", sizeof(abuf) -1);
418 }
419 warnp("%s\n", abuf);
420 }
421
422 /* Adds a label and a prefix to a specific peer */
423 int
424 ldp_peer_add_mapping(struct ldp_peer * p, const struct sockaddr * a,
425 int prefix, int label)
426 {
427 struct label_mapping *lma;
428
429 if (!p)
430 return -1;
431 if ((lma = ldp_peer_get_lm(p, a, prefix)) != NULL) {
432 /* Change the current label */
433 lma->label = label;
434 return LDP_E_OK;
435 }
436
437 lma = malloc(sizeof(*lma));
438
439 if (!lma) {
440 fatalp("ldp_peer_add_mapping: malloc problem\n");
441 return LDP_E_MEMORY;
442 }
443
444 memcpy(&lma->address, a, a->sa_len);
445 lma->prefix = prefix;
446 lma->label = label;
447
448 SLIST_INSERT_HEAD(&p->label_mapping_head, lma, mappings);
449
450 return LDP_E_OK;
451 }
452
453 int
454 ldp_peer_delete_mapping(struct ldp_peer * p, const struct sockaddr * a,
455 int prefix)
456 {
457 struct label_mapping *lma;
458
459 if (!a)
460 return ldp_peer_delete_all_mappings(p);
461
462 lma = ldp_peer_get_lm(p, a, prefix);
463 if (!lma)
464 return LDP_E_NOENT;
465
466 SLIST_REMOVE(&p->label_mapping_head, lma, label_mapping, mappings);
467 free(lma);
468
469 return LDP_E_OK;
470 }
471
472 struct label_mapping *
473 ldp_peer_get_lm(const struct ldp_peer * p, const struct sockaddr * a,
474 uint prefix)
475 {
476 struct label_mapping *rv;
477
478 if (!p)
479 return NULL;
480
481 SLIST_FOREACH(rv, &p->label_mapping_head, mappings)
482 if (rv->prefix == prefix && sockaddr_cmp(a, &rv->address.sa)==0)
483 break;
484
485 return rv;
486
487 }
488
489 int
490 ldp_peer_delete_all_mappings(struct ldp_peer * p)
491 {
492 struct label_mapping *lma;
493
494 while(!SLIST_EMPTY(&p->label_mapping_head)) {
495 lma = SLIST_FIRST(&p->label_mapping_head);
496 SLIST_REMOVE_HEAD(&p->label_mapping_head, mappings);
497 free(lma);
498 }
499
500 return LDP_E_OK;
501 }
502
503 /* returns a mapping and its peer */
504 struct peer_map *
505 ldp_test_mapping(const struct sockaddr * a, int prefix,
506 const struct sockaddr * gate)
507 {
508 struct ldp_peer *lpeer;
509 struct peer_map *rv = NULL;
510 struct label_mapping *lm = NULL;
511
512 /* Checks if it's LPDID, else checks if it's an interface */
513
514 lpeer = get_ldp_peer(gate);
515 if (!lpeer) {
516 debugp("ldp_test_mapping: Gateway is not an LDP peer\n");
517 return NULL;
518 }
519 if (lpeer->state != LDP_PEER_ESTABLISHED) {
520 fatalp("ldp_test_mapping: peer is down ?!\n");
521 return NULL;
522 }
523 lm = ldp_peer_get_lm(lpeer, a, prefix);
524
525 if (!lm) {
526 debugp("Cannot match prefix %s/%d to the specified peer\n",
527 satos(a), prefix);
528 return NULL;
529 }
530 rv = malloc(sizeof(*rv));
531
532 if (!rv) {
533 fatalp("ldp_test_mapping: malloc problem\n");
534 return NULL;
535 }
536
537 rv->lm = lm;
538 rv->peer = lpeer;
539
540 return rv;
541 }
542
543 /* Name from state */
544 const char * ldp_state_to_name(int state)
545 {
546 switch(state) {
547 case LDP_PEER_CONNECTING:
548 return "CONNECTING";
549 case LDP_PEER_CONNECTED:
550 return "CONNECTED";
551 case LDP_PEER_ESTABLISHED:
552 return "ESTABLISHED";
553 case LDP_PEER_HOLDDOWN:
554 return "HOLDDOWN";
555 }
556 return "UNKNOWN";
557 }
558