1 /* $NetBSD: fsm.c,v 1.10 2013/02/05 13:02:33 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 <arpa/inet.h> 35 #include <netinet/in.h> 36 #include <net/if.h> 37 38 #include <ifaddrs.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <strings.h> 42 43 #include "ldp.h" 44 #include "ldp_peer.h" 45 #include "socketops.h" 46 #include "ldp_errors.h" 47 #include "fsm.h" 48 49 char my_ldp_id[20]; 50 struct sockaddr mplssockaddr; 51 52 /* Processing a hello */ 53 void 54 run_ldp_hello(struct ldp_pdu * pduid, struct hello_tlv * ht, 55 struct sockaddr * padd, struct in_addr * ladd, int mysock, bool may_connect) 56 { 57 struct ldp_peer *peer = NULL; 58 struct transport_address_tlv *trtlv; 59 struct hello_info *hi = NULL; 60 union sockunion traddr; 61 62 if ((!pduid) || (!ht)) 63 return; 64 65 debugp("Hello received for address: %s\n", inet_ntoa(*ladd)); 66 debugp("Hello: Type: 0x%.4X Length: %.2d ID: %.8X\n", ht->type, 67 ht->length, ht->messageid); 68 69 if (ht->length <= 4) /* Common hello parameters */ 70 return; 71 ht->ch.type = ntohs(ht->ch.type); 72 ht->ch.length = ntohs(ht->ch.length); 73 ht->ch.holdtime = ntohs(ht->ch.holdtime); 74 ht->ch.res = ntohs(ht->ch.res); 75 debugp("Common hello Type: 0x%.4X Length: %.2d R:%d T:%d" 76 " Hold time: %d\n", ht->ch.type, ht->ch.length, 77 ht->ch.tr / 2, ht->ch.tr % 2, ht->ch.holdtime); 78 79 memset(&traddr, 0, sizeof(traddr)); 80 /* Check transport TLV */ 81 if (pduid->length - PDU_PAYLOAD_LENGTH - 82 sizeof(struct hello_tlv) >= 8) { 83 trtlv = (struct transport_address_tlv *)(ht + 1); 84 if (trtlv->type == htons(TLV_IPV4_TRANSPORT)) { 85 traddr.sin.sin_family = AF_INET; 86 traddr.sin.sin_len = sizeof(struct sockaddr_in); 87 memcpy(&traddr.sin.sin_addr, 88 &trtlv->address, sizeof(struct in_addr)); 89 } else if (trtlv->type == htons(TLV_IPV6_TRANSPORT)) { 90 traddr.sin6.sin6_family = AF_INET6; 91 traddr.sin6.sin6_len = sizeof(struct sockaddr_in6); 92 memcpy(&traddr.sin6.sin6_addr, 93 &trtlv->address, sizeof(struct in6_addr)); 94 } else 95 warnp("Unknown AF %x for transport address\n", 96 ntohs(trtlv->type)); 97 } else { 98 /* Use LDP ID as transport address */ 99 traddr.sin.sin_family = AF_INET; 100 traddr.sin.sin_len = sizeof(struct sockaddr_in); 101 memcpy(&traddr.sin.sin_addr, 102 &pduid->ldp_id, sizeof(struct in_addr)); 103 } 104 /* Add it to hello list or just update timer */ 105 SLIST_FOREACH(hi, &hello_info_head, infos) 106 if (hi->ldp_id.s_addr == pduid->ldp_id.s_addr && 107 sockaddr_cmp(&hi->transport_address.sa, &traddr.sa) == 0) 108 break; 109 if (hi == NULL) { 110 hi = calloc(1, sizeof(*hi)); 111 if (!hi) { 112 fatalp("Cannot alloc a hello info structure"); 113 return; 114 } 115 hi->ldp_id.s_addr = pduid->ldp_id.s_addr; 116 memcpy(&hi->transport_address, &traddr, traddr.sa.sa_len); 117 SLIST_INSERT_HEAD(&hello_info_head, hi, infos); 118 } 119 120 /* Update expire timer */ 121 if (ht->ch.holdtime != 0) 122 hi->keepalive = ht->ch.holdtime; 123 else { 124 if (ht->ch.res >> 15 == 0) 125 hi->keepalive = LDP_HELLO_KEEP; 126 else 127 hi->keepalive = LDP_THELLO_KEEP; 128 } 129 130 if (!get_ldp_peer_by_id(&pduid->ldp_id)) { 131 /* 132 * RFC 5036 2.5.2: If A1 > A2, LSR1 plays the active role; 133 * otherwise it is passive. 134 */ 135 if (may_connect == true && 136 (hi->transport_address.sa.sa_family == AF_INET && 137 ntohl(hi->transport_address.sin.sin_addr.s_addr) < 138 ntohl(ladd->s_addr))) { 139 peer = ldp_peer_new(&pduid->ldp_id, padd, 140 &hi->transport_address.sa, 141 ht->ch.holdtime, 0); 142 if (peer == NULL) 143 return; 144 if (peer->state == LDP_PEER_CONNECTED) 145 send_initialize(peer); 146 } 147 } 148 } 149 150 struct address_list_tlv * 151 build_address_list_tlv(void) 152 { 153 struct address_list_tlv *t; 154 struct ifaddrs *ifa, *ifb; 155 struct sockaddr_in *sa; 156 struct in_addr *ia; 157 uint16_t adrcount = 0; 158 159 if (getifaddrs(&ifa) == -1) 160 return NULL; 161 162 /* Find out the number of addresses */ 163 /* Ignore loopback */ 164 for (ifb = ifa; ifb; ifb = ifb->ifa_next) 165 if ((ifb->ifa_addr->sa_family == AF_INET) && 166 (ifb->ifa_flags & IFF_UP)) { 167 sa = (struct sockaddr_in *) ifb->ifa_addr; 168 if (ntohl(sa->sin_addr.s_addr) >> 24 != IN_LOOPBACKNET) 169 adrcount++; 170 } 171 t = malloc(sizeof(*t) + (adrcount - 1) * sizeof(struct in_addr)); 172 173 if (!t) { 174 fatalp("build_address_list_tlv: malloc problem\n"); 175 freeifaddrs(ifa); 176 return NULL; 177 } 178 179 t->type = htons(LDP_ADDRESS); 180 t->length = htons(sizeof(struct address_list_tlv) - TLV_TYPE_LENGTH 181 + (adrcount - 1) * sizeof(struct in_addr)); 182 t->messageid = htonl(get_message_id()); 183 184 t->a_type = htons(TLV_ADDRESS_LIST); 185 t->a_length = htons(sizeof(t->a_af) + 186 adrcount * sizeof(struct in_addr)); 187 t->a_af = htons(LDP_AF_INET); 188 189 ia = &t->a_address; 190 for (adrcount = 0, ifb = ifa; ifb; ifb = ifb->ifa_next) { 191 if ((ifb->ifa_addr->sa_family != AF_INET) || 192 (!(ifb->ifa_flags & IFF_UP)) || 193 (ifb->ifa_flags & IFF_LOOPBACK)) 194 continue; 195 sa = (struct sockaddr_in *) ifb->ifa_addr; 196 memcpy(&ia[adrcount], &sa->sin_addr, sizeof(struct in_addr)); 197 adrcount++; 198 } 199 freeifaddrs(ifa); 200 201 add_my_if_addrs(ia, adrcount); 202 return t; 203 } 204 205 /* 206 * Calculate LDP ID 207 * Get also mpls pseudo-interface address 208 */ 209 int 210 set_my_ldp_id() 211 { 212 struct ifaddrs *ifa, *ifb; 213 struct in_addr a; 214 struct sockaddr_in *sa; 215 216 a.s_addr = 0; 217 my_ldp_id[0] = '\0'; 218 mplssockaddr.sa_len = 0; 219 220 if (getifaddrs(&ifa) == -1) 221 return LDP_E_GENERIC; 222 223 for (ifb = ifa; ifb; ifb = ifb->ifa_next) 224 if(ifb->ifa_flags & IFF_UP) { 225 if (strncmp("mpls", ifb->ifa_name, 4) == 0 && 226 ifb->ifa_addr->sa_family == AF_LINK) 227 memcpy(&mplssockaddr, ifb->ifa_addr, 228 ifb->ifa_addr->sa_len); 229 230 if (ifb->ifa_addr->sa_family != AF_INET) 231 continue; 232 233 sa = (struct sockaddr_in *) ifb->ifa_addr; 234 if (ntohl(sa->sin_addr.s_addr) >> 24 == IN_LOOPBACKNET) 235 continue; /* No 127/8 */ 236 if (ntohl(sa->sin_addr.s_addr) > ntohl(a.s_addr)) 237 a.s_addr = sa->sin_addr.s_addr; 238 } 239 freeifaddrs(ifa); 240 debugp("LDP ID: %s\n", inet_ntoa(a)); 241 strlcpy(my_ldp_id, inet_ntoa(a), INET_ADDRSTRLEN); 242 return LDP_E_OK; 243 } 244