xref: /openbsd-src/usr.sbin/ldpd/address.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: address.c,v 1.30 2016/09/03 16:07:08 renato Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <arpa/inet.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "ldpd.h"
25 #include "ldpe.h"
26 #include "lde.h"
27 #include "log.h"
28 
29 static void	 send_address(struct nbr *, int, struct if_addr_head *,
30 		    unsigned int, int);
31 static int	 gen_address_list_tlv(struct ibuf *, uint16_t, int,
32 		    struct if_addr_head *, unsigned int);
33 static void	 address_list_add(struct if_addr_head *, struct if_addr *);
34 static void	 address_list_clr(struct if_addr_head *);
35 
36 static void
37 send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list,
38     unsigned int addr_count, int withdraw)
39 {
40 	struct ibuf	*buf;
41 	uint16_t	 msg_type;
42 	uint8_t		 addr_size;
43 	struct if_addr	*if_addr;
44 	uint16_t	 size;
45 	unsigned int	 tlv_addr_count = 0;
46 	int		 err = 0;
47 
48 	/* nothing to send */
49 	if (LIST_EMPTY(addr_list))
50 		return;
51 
52 	if (!withdraw)
53 		msg_type = MSG_TYPE_ADDR;
54 	else
55 		msg_type = MSG_TYPE_ADDRWITHDRAW;
56 
57 	switch (af) {
58 	case AF_INET:
59 		addr_size = sizeof(struct in_addr);
60 		break;
61 	case AF_INET6:
62 		addr_size = sizeof(struct in6_addr);
63 		break;
64 	default:
65 		fatalx("send_address: unknown af");
66 	}
67 
68 	while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
69 		/*
70 		 * Send as many addresses as possible - respect the session's
71 		 * negotiated maximum pdu length.
72 		 */
73 		size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE;
74 		if (size + addr_count * addr_size <= nbr->max_pdu_len)
75 			tlv_addr_count = addr_count;
76 		else
77 			tlv_addr_count = (nbr->max_pdu_len - size) / addr_size;
78 		size += tlv_addr_count * addr_size;
79 		addr_count -= tlv_addr_count;
80 
81 		if ((buf = ibuf_open(size)) == NULL)
82 			fatal(__func__);
83 
84 		err |= gen_ldp_hdr(buf, size);
85 		size -= LDP_HDR_SIZE;
86 		err |= gen_msg_hdr(buf, msg_type, size);
87 		size -= LDP_MSG_SIZE;
88 		err |= gen_address_list_tlv(buf, size, af, addr_list,
89 		    tlv_addr_count);
90 		if (err) {
91 			address_list_clr(addr_list);
92 			ibuf_free(buf);
93 			return;
94 		}
95 
96 		while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
97 			log_debug("msg-out: %s: lsr-id %s, address %s",
98 			    msg_name(msg_type), inet_ntoa(nbr->id),
99 			    log_addr(af, &if_addr->addr));
100 
101 			LIST_REMOVE(if_addr, entry);
102 			free(if_addr);
103 			if (--tlv_addr_count == 0)
104 				break;
105 		}
106 
107 		evbuf_enqueue(&nbr->tcp->wbuf, buf);
108 	}
109 
110 	nbr_fsm(nbr, NBR_EVT_PDU_SENT);
111 }
112 
113 void
114 send_address_single(struct nbr *nbr, struct if_addr *if_addr, int withdraw)
115 {
116 	struct if_addr_head	 addr_list;
117 
118 	LIST_INIT(&addr_list);
119 	address_list_add(&addr_list, if_addr);
120 	send_address(nbr, if_addr->af, &addr_list, 1, withdraw);
121 }
122 
123 void
124 send_address_all(struct nbr *nbr, int af)
125 {
126 	struct if_addr_head	 addr_list;
127 	struct if_addr		*if_addr;
128 	unsigned int		 addr_count = 0;
129 
130 	LIST_INIT(&addr_list);
131 	LIST_FOREACH(if_addr, &global.addr_list, entry) {
132 		if (if_addr->af != af)
133 			continue;
134 
135 		address_list_add(&addr_list, if_addr);
136 		addr_count++;
137 	}
138 
139 	send_address(nbr, af, &addr_list, addr_count, 0);
140 }
141 
142 int
143 recv_address(struct nbr *nbr, char *buf, uint16_t len)
144 {
145 	struct ldp_msg		msg;
146 	uint16_t		msg_type;
147 	struct address_list_tlv	alt;
148 	enum imsg_type		type;
149 	struct lde_addr		lde_addr;
150 
151 	memcpy(&msg, buf, sizeof(msg));
152 	buf += LDP_MSG_SIZE;
153 	len -= LDP_MSG_SIZE;
154 
155 	/* Address List TLV */
156 	if (len < ADDR_LIST_SIZE) {
157 		session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
158 		return (-1);
159 	}
160 
161 	memcpy(&alt, buf, sizeof(alt));
162 	if (ntohs(alt.length) != len - TLV_HDR_SIZE) {
163 		session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
164 		return (-1);
165 	}
166 	if (ntohs(alt.type) != TLV_TYPE_ADDRLIST) {
167 		session_shutdown(nbr, S_UNKNOWN_TLV, msg.id, msg.type);
168 		return (-1);
169 	}
170 	switch (ntohs(alt.family)) {
171 	case AF_IPV4:
172 		if (!nbr->v4_enabled)
173 			/* just ignore the message */
174 			return (0);
175 		break;
176 	case AF_IPV6:
177 		if (!nbr->v6_enabled)
178 			/* just ignore the message */
179 			return (0);
180 		break;
181 	default:
182 		send_notification_nbr(nbr, S_UNSUP_ADDR, msg.id, msg.type);
183 		return (-1);
184 	}
185 	buf += sizeof(alt);
186 	len -= sizeof(alt);
187 
188 	msg_type = ntohs(msg.type);
189 	if (msg_type == MSG_TYPE_ADDR)
190 		type = IMSG_ADDRESS_ADD;
191 	else
192 		type = IMSG_ADDRESS_DEL;
193 
194 	while (len > 0) {
195 		switch (ntohs(alt.family)) {
196 		case AF_IPV4:
197 			if (len < sizeof(struct in_addr)) {
198 				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
199 				    msg.type);
200 				return (-1);
201 			}
202 
203 			memset(&lde_addr, 0, sizeof(lde_addr));
204 			lde_addr.af = AF_INET;
205 			memcpy(&lde_addr.addr, buf, sizeof(struct in_addr));
206 
207 			buf += sizeof(struct in_addr);
208 			len -= sizeof(struct in_addr);
209 			break;
210 		case AF_IPV6:
211 			if (len < sizeof(struct in6_addr)) {
212 				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
213 				    msg.type);
214 				return (-1);
215 			}
216 
217 			memset(&lde_addr, 0, sizeof(lde_addr));
218 			lde_addr.af = AF_INET6;
219 			memcpy(&lde_addr.addr, buf, sizeof(struct in6_addr));
220 
221 			buf += sizeof(struct in6_addr);
222 			len -= sizeof(struct in6_addr);
223 			break;
224 		default:
225 			fatalx("recv_address: unknown af");
226 		}
227 
228 		log_debug("msg-in: %s: lsr-id %s, address %s",
229 		    msg_name(msg_type), inet_ntoa(nbr->id),
230 		    log_addr(lde_addr.af, &lde_addr.addr));
231 
232 		ldpe_imsg_compose_lde(type, nbr->peerid, 0, &lde_addr,
233 		    sizeof(lde_addr));
234 	}
235 
236 	return (0);
237 }
238 
239 static int
240 gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af,
241     struct if_addr_head *addr_list, unsigned int tlv_addr_count)
242 {
243 	struct address_list_tlv	 alt;
244 	uint16_t		 addr_size;
245 	struct if_addr		*if_addr;
246 	int			 err = 0;
247 
248 	memset(&alt, 0, sizeof(alt));
249 	alt.type = TLV_TYPE_ADDRLIST;
250 	alt.length = htons(size - TLV_HDR_SIZE);
251 
252 	switch (af) {
253 	case AF_INET:
254 		alt.family = htons(AF_IPV4);
255 		addr_size = sizeof(struct in_addr);
256 		break;
257 	case AF_INET6:
258 		alt.family = htons(AF_IPV6);
259 		addr_size = sizeof(struct in6_addr);
260 		break;
261 	default:
262 		fatalx("gen_address_list_tlv: unknown af");
263 	}
264 
265 	err |= ibuf_add(buf, &alt, sizeof(alt));
266 	LIST_FOREACH(if_addr, addr_list, entry) {
267 		err |= ibuf_add(buf, &if_addr->addr, addr_size);
268 		if (--tlv_addr_count == 0)
269 			break;
270 	}
271 
272 	return (err);
273 }
274 
275 static void
276 address_list_add(struct if_addr_head *addr_list, struct if_addr *if_addr)
277 {
278 	struct if_addr		*new;
279 
280 	new = malloc(sizeof(*new));
281 	if (new == NULL)
282 		fatal(__func__);
283 	*new = *if_addr;
284 
285 	LIST_INSERT_HEAD(addr_list, new, entry);
286 }
287 
288 static void
289 address_list_clr(struct if_addr_head *addr_list)
290 {
291 	struct if_addr		*if_addr;
292 
293 	while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
294 		LIST_REMOVE(if_addr, entry);
295 		free(if_addr);
296 	}
297 }
298