1/*	$OpenBSD: address.c,v 1.35 2017/03/04 00:21:48 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
29static void	 send_address(struct nbr *, int, struct if_addr_head *,
30		    unsigned int, int);
31static int	 gen_address_list_tlv(struct ibuf *, int, struct if_addr_head *,
32		    unsigned int);
33static int	 gen_mac_list_tlv(struct ibuf *, uint8_t *);
34static void	 address_list_add(struct if_addr_head *, struct if_addr *);
35static void	 address_list_clr(struct if_addr_head *);
36static void	 log_msg_address(int, uint16_t, struct nbr *, int,
37		    union ldpd_addr *);
38static void	 log_msg_mac_withdrawal(int, struct nbr *, uint8_t *);
39
40static void
41send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list,
42    unsigned int addr_count, int withdraw)
43{
44	struct ibuf	*buf;
45	uint16_t	 msg_type;
46	uint8_t		 addr_size;
47	struct if_addr	*if_addr;
48	uint16_t	 size;
49	unsigned int	 tlv_addr_count = 0;
50	int		 err = 0;
51
52	/* nothing to send */
53	if (LIST_EMPTY(addr_list))
54		return;
55
56	if (!withdraw)
57		msg_type = MSG_TYPE_ADDR;
58	else
59		msg_type = MSG_TYPE_ADDRWITHDRAW;
60
61	switch (af) {
62	case AF_INET:
63		addr_size = sizeof(struct in_addr);
64		break;
65	case AF_INET6:
66		addr_size = sizeof(struct in6_addr);
67		break;
68	default:
69		fatalx("send_address: unknown af");
70	}
71
72	while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
73		/*
74		 * Send as many addresses as possible - respect the session's
75		 * negotiated maximum pdu length.
76		 */
77		size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE;
78		if (size + addr_count * addr_size <= nbr->max_pdu_len)
79			tlv_addr_count = addr_count;
80		else
81			tlv_addr_count = (nbr->max_pdu_len - size) / addr_size;
82		size += tlv_addr_count * addr_size;
83		addr_count -= tlv_addr_count;
84
85		if ((buf = ibuf_open(size)) == NULL)
86			fatal(__func__);
87
88		err |= gen_ldp_hdr(buf, size);
89		size -= LDP_HDR_SIZE;
90		err |= gen_msg_hdr(buf, msg_type, size);
91		size -= LDP_MSG_SIZE;
92		err |= gen_address_list_tlv(buf, af, addr_list, tlv_addr_count);
93		if (err) {
94			address_list_clr(addr_list);
95			ibuf_free(buf);
96			return;
97		}
98
99		while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
100			log_msg_address(1, msg_type, nbr, af, &if_addr->addr);
101
102			LIST_REMOVE(if_addr, entry);
103			free(if_addr);
104			if (--tlv_addr_count == 0)
105				break;
106		}
107
108		evbuf_enqueue(&nbr->tcp->wbuf, buf);
109	}
110
111	nbr_fsm(nbr, NBR_EVT_PDU_SENT);
112}
113
114void
115send_address_single(struct nbr *nbr, struct if_addr *if_addr, int withdraw)
116{
117	struct if_addr_head	 addr_list;
118
119	LIST_INIT(&addr_list);
120	address_list_add(&addr_list, if_addr);
121	send_address(nbr, if_addr->af, &addr_list, 1, withdraw);
122}
123
124void
125send_address_all(struct nbr *nbr, int af)
126{
127	struct if_addr_head	 addr_list;
128	struct if_addr		*if_addr;
129	unsigned int		 addr_count = 0;
130
131	LIST_INIT(&addr_list);
132	LIST_FOREACH(if_addr, &global.addr_list, entry) {
133		if (if_addr->af != af)
134			continue;
135
136		address_list_add(&addr_list, if_addr);
137		addr_count++;
138	}
139
140	send_address(nbr, af, &addr_list, addr_count, 0);
141}
142
143void
144send_mac_withdrawal(struct nbr *nbr, struct map *fec, uint8_t *mac)
145{
146	struct ibuf	*buf;
147	uint16_t	 size;
148	int		 err;
149
150	size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE + len_fec_tlv(fec) +
151	    TLV_HDR_SIZE;
152	if (mac)
153		size += ETHER_ADDR_LEN;
154
155	if ((buf = ibuf_open(size)) == NULL)
156		fatal(__func__);
157
158	err = gen_ldp_hdr(buf, size);
159	size -= LDP_HDR_SIZE;
160	err |= gen_msg_hdr(buf, MSG_TYPE_ADDRWITHDRAW, size);
161	size -= LDP_MSG_SIZE;
162	err |= gen_address_list_tlv(buf, AF_INET, NULL, 0);
163	err |= gen_fec_tlv(buf, fec);
164	err |= gen_mac_list_tlv(buf, mac);
165	if (err) {
166		ibuf_free(buf);
167		return;
168	}
169
170	log_msg_mac_withdrawal(1, nbr, mac);
171
172	evbuf_enqueue(&nbr->tcp->wbuf, buf);
173
174	nbr_fsm(nbr, NBR_EVT_PDU_SENT);
175}
176
177int
178recv_address(struct nbr *nbr, char *buf, uint16_t len)
179{
180	struct ldp_msg		msg;
181	uint16_t		msg_type;
182	enum imsg_type		type;
183	struct address_list_tlv	alt;
184	uint16_t		alt_len;
185	uint16_t		alt_family;
186	struct lde_addr		lde_addr;
187
188	memcpy(&msg, buf, sizeof(msg));
189	msg_type = ntohs(msg.type);
190	switch (msg_type) {
191	case MSG_TYPE_ADDR:
192		type = IMSG_ADDRESS_ADD;
193		break;
194	case MSG_TYPE_ADDRWITHDRAW:
195		type = IMSG_ADDRESS_DEL;
196		break;
197	default:
198		fatalx("recv_address: unexpected msg type");
199	}
200	buf += LDP_MSG_SIZE;
201	len -= LDP_MSG_SIZE;
202
203	/* Address List TLV */
204	if (len < ADDR_LIST_SIZE) {
205		session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
206		return (-1);
207	}
208	memcpy(&alt, buf, sizeof(alt));
209	alt_len = ntohs(alt.length);
210	alt_family = ntohs(alt.family);
211	if (alt_len > len - TLV_HDR_SIZE) {
212		session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
213		return (-1);
214	}
215	if (ntohs(alt.type) != TLV_TYPE_ADDRLIST) {
216		send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
217		return (-1);
218	}
219	switch (alt_family) {
220	case AF_IPV4:
221		if (!nbr->v4_enabled)
222			/* just ignore the message */
223			return (0);
224		break;
225	case AF_IPV6:
226		if (!nbr->v6_enabled)
227			/* just ignore the message */
228			return (0);
229		break;
230	default:
231		send_notification(nbr->tcp, S_UNSUP_ADDR, msg.id, msg.type);
232		return (-1);
233	}
234	alt_len -= sizeof(alt.family);
235	buf += sizeof(alt);
236	len -= sizeof(alt);
237
238	/* Process all received addresses */
239	while (alt_len > 0) {
240		switch (alt_family) {
241		case AF_IPV4:
242			if (alt_len < sizeof(struct in_addr)) {
243				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
244				    msg.type);
245				return (-1);
246			}
247
248			memset(&lde_addr, 0, sizeof(lde_addr));
249			lde_addr.af = AF_INET;
250			memcpy(&lde_addr.addr, buf, sizeof(struct in_addr));
251
252			buf += sizeof(struct in_addr);
253			len -= sizeof(struct in_addr);
254			alt_len -= sizeof(struct in_addr);
255			break;
256		case AF_IPV6:
257			if (alt_len < sizeof(struct in6_addr)) {
258				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
259				    msg.type);
260				return (-1);
261			}
262
263			memset(&lde_addr, 0, sizeof(lde_addr));
264			lde_addr.af = AF_INET6;
265			memcpy(&lde_addr.addr, buf, sizeof(struct in6_addr));
266
267			buf += sizeof(struct in6_addr);
268			len -= sizeof(struct in6_addr);
269			alt_len -= sizeof(struct in6_addr);
270			break;
271		default:
272			fatalx("recv_address: unknown af");
273		}
274
275		log_msg_address(0, msg_type, nbr, lde_addr.af, &lde_addr.addr);
276
277		ldpe_imsg_compose_lde(type, nbr->peerid, 0, &lde_addr,
278		    sizeof(lde_addr));
279	}
280
281	/* Optional Parameters */
282	while (len > 0) {
283		struct tlv 	tlv;
284		uint16_t	tlv_type;
285		uint16_t	tlv_len;
286
287		if (len < sizeof(tlv)) {
288			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
289			return (-1);
290		}
291
292		memcpy(&tlv, buf, TLV_HDR_SIZE);
293		tlv_type = ntohs(tlv.type);
294		tlv_len = ntohs(tlv.length);
295		if (tlv_len + TLV_HDR_SIZE > len) {
296			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
297			return (-1);
298		}
299		buf += TLV_HDR_SIZE;
300		len -= TLV_HDR_SIZE;
301
302		switch (tlv_type) {
303		default:
304			if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
305				send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
306				    msg.id, msg.type, tlv_type, tlv_len, buf);
307			/* ignore unknown tlv */
308			break;
309		}
310		buf += tlv_len;
311		len -= tlv_len;
312	}
313
314	return (0);
315}
316
317static int
318gen_address_list_tlv(struct ibuf *buf, int af, struct if_addr_head *addr_list,
319    unsigned int tlv_addr_count)
320{
321	struct address_list_tlv	 alt;
322	uint16_t		 addr_size;
323	struct if_addr		*if_addr;
324	int			 err = 0;
325
326	memset(&alt, 0, sizeof(alt));
327	alt.type = htons(TLV_TYPE_ADDRLIST);
328
329	switch (af) {
330	case AF_INET:
331		alt.family = htons(AF_IPV4);
332		addr_size = sizeof(struct in_addr);
333		break;
334	case AF_INET6:
335		alt.family = htons(AF_IPV6);
336		addr_size = sizeof(struct in6_addr);
337		break;
338	default:
339		fatalx("gen_address_list_tlv: unknown af");
340	}
341	alt.length = htons(sizeof(alt.family) + addr_size * tlv_addr_count);
342
343	err |= ibuf_add(buf, &alt, sizeof(alt));
344	if (addr_list == NULL)
345		return (err);
346
347	LIST_FOREACH(if_addr, addr_list, entry) {
348		err |= ibuf_add(buf, &if_addr->addr, addr_size);
349		if (--tlv_addr_count == 0)
350			break;
351	}
352
353	return (err);
354}
355
356static int
357gen_mac_list_tlv(struct ibuf *buf, uint8_t *mac)
358{
359	struct tlv	 tlv;
360	int		 err;
361
362	memset(&tlv, 0, sizeof(tlv));
363	tlv.type = htons(TLV_TYPE_MAC_LIST);
364	if (mac)
365		tlv.length = htons(ETHER_ADDR_LEN);
366	err = ibuf_add(buf, &tlv, sizeof(tlv));
367	if (mac)
368		err |= ibuf_add(buf, mac, ETHER_ADDR_LEN);
369
370	return (err);
371}
372
373static void
374address_list_add(struct if_addr_head *addr_list, struct if_addr *if_addr)
375{
376	struct if_addr		*new;
377
378	new = malloc(sizeof(*new));
379	if (new == NULL)
380		fatal(__func__);
381	*new = *if_addr;
382
383	LIST_INSERT_HEAD(addr_list, new, entry);
384}
385
386static void
387address_list_clr(struct if_addr_head *addr_list)
388{
389	struct if_addr		*if_addr;
390
391	while ((if_addr = LIST_FIRST(addr_list)) != NULL) {
392		LIST_REMOVE(if_addr, entry);
393		free(if_addr);
394	}
395}
396
397static void
398log_msg_address(int out, uint16_t msg_type, struct nbr *nbr, int af,
399    union ldpd_addr *addr)
400{
401	log_debug("msg-%s: %s: lsr-id %s, address %s", (out) ? "out" : "in",
402	    msg_name(msg_type), inet_ntoa(nbr->id), log_addr(af, addr));
403}
404
405static void
406log_msg_mac_withdrawal(int out, struct nbr *nbr, uint8_t *mac)
407{
408	log_debug("msg-%s: mac withdrawal: lsr-id %s, mac %s",
409	    (out) ? "out" : "in", inet_ntoa(nbr->id),
410	    (mac) ? ether_ntoa((struct ether_addr *)mac) : "wildcard");
411}
412