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