Home | History | Annotate | Line # | Download | only in ldpd
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