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