1/*	$OpenBSD: init.c,v 1.37 2017/03/04 00:15:35 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 <string.h>
22
23#include "ldpd.h"
24#include "ldpe.h"
25#include "log.h"
26
27static int	gen_init_prms_tlv(struct ibuf *, struct nbr *);
28static int	gen_cap_dynamic_tlv(struct ibuf *);
29static int	gen_cap_twcard_tlv(struct ibuf *, int);
30static int	gen_cap_unotif_tlv(struct ibuf *, int);
31
32void
33send_init(struct nbr *nbr)
34{
35	struct ibuf		*buf;
36	uint16_t		 size;
37	int			 err = 0;
38
39	log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
40
41	size = LDP_HDR_SIZE + LDP_MSG_SIZE + SESS_PRMS_SIZE +
42	    CAP_TLV_DYNAMIC_SIZE + CAP_TLV_TWCARD_SIZE + CAP_TLV_UNOTIF_SIZE;
43	if ((buf = ibuf_open(size)) == NULL)
44		fatal(__func__);
45
46	err |= gen_ldp_hdr(buf, size);
47	size -= LDP_HDR_SIZE;
48	err |= gen_msg_hdr(buf, MSG_TYPE_INIT, size);
49	err |= gen_init_prms_tlv(buf, nbr);
50	err |= gen_cap_dynamic_tlv(buf);
51	err |= gen_cap_twcard_tlv(buf, 1);
52	err |= gen_cap_unotif_tlv(buf, 1);
53	if (err) {
54		ibuf_free(buf);
55		return;
56	}
57
58	evbuf_enqueue(&nbr->tcp->wbuf, buf);
59}
60
61int
62recv_init(struct nbr *nbr, char *buf, uint16_t len)
63{
64	struct ldp_msg		msg;
65	struct sess_prms_tlv	sess;
66	uint16_t		max_pdu_len;
67	int			caps_rcvd = 0;
68
69	log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
70
71	memcpy(&msg, buf, sizeof(msg));
72	buf += LDP_MSG_SIZE;
73	len -= LDP_MSG_SIZE;
74
75	if (len < SESS_PRMS_SIZE) {
76		session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
77		return (-1);
78	}
79	memcpy(&sess, buf, sizeof(sess));
80	if (ntohs(sess.length) != SESS_PRMS_LEN) {
81		session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
82		return (-1);
83	}
84	if (ntohs(sess.proto_version) != LDP_VERSION) {
85		session_shutdown(nbr, S_BAD_PROTO_VER, msg.id, msg.type);
86		return (-1);
87	}
88	if (ntohs(sess.keepalive_time) < MIN_KEEPALIVE) {
89		session_shutdown(nbr, S_KEEPALIVE_BAD, msg.id, msg.type);
90		return (-1);
91	}
92	if (sess.lsr_id != leconf->rtr_id.s_addr ||
93	    ntohs(sess.lspace_id) != 0) {
94		session_shutdown(nbr, S_NO_HELLO, msg.id, msg.type);
95		return (-1);
96	}
97
98	buf += SESS_PRMS_SIZE;
99	len -= SESS_PRMS_SIZE;
100
101	/* Optional Parameters */
102	while (len > 0) {
103		struct tlv 	tlv;
104		uint16_t	tlv_type;
105		uint16_t	tlv_len;
106
107		if (len < sizeof(tlv)) {
108			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
109			return (-1);
110		}
111
112		memcpy(&tlv, buf, TLV_HDR_SIZE);
113		tlv_type = ntohs(tlv.type);
114		tlv_len = ntohs(tlv.length);
115		if (tlv_len + TLV_HDR_SIZE > len) {
116			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
117			return (-1);
118		}
119		buf += TLV_HDR_SIZE;
120		len -= TLV_HDR_SIZE;
121
122		/*
123		 * RFC 5561 - Section 6:
124		 * "The S-bit of a Capability Parameter in an Initialization
125		 * message MUST be 1 and SHOULD be ignored on receipt".
126		 */
127		switch (tlv_type) {
128		case TLV_TYPE_ATMSESSIONPAR:
129			session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
130			return (-1);
131		case TLV_TYPE_FRSESSION:
132			session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
133			return (-1);
134		case TLV_TYPE_DYNAMIC_CAP:
135			if (tlv_len != CAP_TLV_DYNAMIC_LEN) {
136				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
137				    msg.type);
138				return (-1);
139			}
140
141			if (caps_rcvd & F_CAP_TLV_RCVD_DYNAMIC) {
142				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
143				    msg.type);
144				return (-1);
145			}
146			caps_rcvd |= F_CAP_TLV_RCVD_DYNAMIC;
147
148			nbr->flags |= F_NBR_CAP_DYNAMIC;
149
150			log_debug("%s: lsr-id %s announced the Dynamic "
151			    "Capability Announcement capability", __func__,
152			    inet_ntoa(nbr->id));
153			break;
154		case TLV_TYPE_TWCARD_CAP:
155			if (tlv_len != CAP_TLV_TWCARD_LEN) {
156				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
157				    msg.type);
158				return (-1);
159			}
160
161			if (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) {
162				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
163				    msg.type);
164				return (-1);
165			}
166			caps_rcvd |= F_CAP_TLV_RCVD_TWCARD;
167
168			nbr->flags |= F_NBR_CAP_TWCARD;
169
170			log_debug("%s: lsr-id %s announced the Typed Wildcard "
171			    "FEC capability", __func__, inet_ntoa(nbr->id));
172			break;
173		case TLV_TYPE_UNOTIF_CAP:
174			if (tlv_len != CAP_TLV_UNOTIF_LEN) {
175				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
176				    msg.type);
177				return (-1);
178			}
179
180			if (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) {
181				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
182				    msg.type);
183				return (-1);
184			}
185			caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF;
186
187			nbr->flags |= F_NBR_CAP_UNOTIF;
188
189			log_debug("%s: lsr-id %s announced the Unrecognized "
190			    "Notification capability", __func__,
191			    inet_ntoa(nbr->id));
192			break;
193		default:
194			if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
195				send_notification_rtlvs(nbr, S_UNSSUPORTDCAP,
196				    msg.id, msg.type, tlv_type, tlv_len, buf);
197			/* ignore unknown tlv */
198			break;
199		}
200		buf += tlv_len;
201		len -= tlv_len;
202	}
203
204	nbr->keepalive = min(nbr_get_keepalive(nbr->af, nbr->id),
205	    ntohs(sess.keepalive_time));
206
207	max_pdu_len = ntohs(sess.max_pdu_len);
208	/*
209	 * RFC 5036 - Section 3.5.3:
210	 * "A value of 255 or less specifies the default maximum length of
211	 * 4096 octets".
212	 */
213	if (max_pdu_len <= 255)
214		max_pdu_len = LDP_MAX_LEN;
215	nbr->max_pdu_len = min(max_pdu_len, LDP_MAX_LEN);
216
217	nbr_fsm(nbr, NBR_EVT_INIT_RCVD);
218
219	return (0);
220}
221
222void
223send_capability(struct nbr *nbr, uint16_t capability, int enable)
224{
225	struct ibuf		*buf;
226	uint16_t		 size;
227	int			 err = 0;
228
229	log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
230
231	size = LDP_HDR_SIZE + LDP_MSG_SIZE + CAP_TLV_DYNAMIC_SIZE;
232	if ((buf = ibuf_open(size)) == NULL)
233		fatal(__func__);
234
235	err |= gen_ldp_hdr(buf, size);
236	size -= LDP_HDR_SIZE;
237	err |= gen_msg_hdr(buf, MSG_TYPE_CAPABILITY, size);
238
239	switch (capability) {
240	case TLV_TYPE_TWCARD_CAP:
241		err |= gen_cap_twcard_tlv(buf, enable);
242		break;
243	case TLV_TYPE_UNOTIF_CAP:
244		err |= gen_cap_unotif_tlv(buf, enable);
245		break;
246	case TLV_TYPE_DYNAMIC_CAP:
247		/*
248		 * RFC 5561 - Section 9:
249		 * "An LDP speaker MUST NOT include the Dynamic Capability
250		 * Announcement Parameter in Capability messages sent to
251		 * its peers".
252		 */
253		/* FALLTHROUGH */
254	default:
255		fatalx("send_capability: unsupported capability");
256	}
257
258	if (err) {
259		ibuf_free(buf);
260		return;
261	}
262
263	evbuf_enqueue(&nbr->tcp->wbuf, buf);
264	nbr_fsm(nbr, NBR_EVT_PDU_SENT);
265}
266
267int
268recv_capability(struct nbr *nbr, char *buf, uint16_t len)
269{
270	struct ldp_msg	 msg;
271	int		 enable = 0;
272	int		 caps_rcvd = 0;
273
274	log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
275
276	memcpy(&msg, buf, sizeof(msg));
277	buf += LDP_MSG_SIZE;
278	len -= LDP_MSG_SIZE;
279
280	/* Optional Parameters */
281	while (len > 0) {
282		struct tlv 	 tlv;
283		uint16_t	 tlv_type;
284		uint16_t	 tlv_len;
285		uint8_t		 reserved;
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		case TLV_TYPE_TWCARD_CAP:
304			if (tlv_len != CAP_TLV_TWCARD_LEN) {
305				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
306				    msg.type);
307				return (-1);
308			}
309
310			if (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) {
311				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
312				    msg.type);
313				return (-1);
314			}
315			caps_rcvd |= F_CAP_TLV_RCVD_TWCARD;
316
317			memcpy(&reserved, buf, sizeof(reserved));
318			enable = reserved & STATE_BIT;
319			if (enable)
320				nbr->flags |= F_NBR_CAP_TWCARD;
321			else
322				nbr->flags &= ~F_NBR_CAP_TWCARD;
323
324			log_debug("%s: lsr-id %s %s the Typed Wildcard FEC "
325			    "capability", __func__, inet_ntoa(nbr->id),
326			    (enable) ? "announced" : "withdrew");
327			break;
328		case TLV_TYPE_UNOTIF_CAP:
329			if (tlv_len != CAP_TLV_UNOTIF_LEN) {
330				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
331				    msg.type);
332				return (-1);
333			}
334
335			if (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) {
336				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
337				    msg.type);
338				return (-1);
339			}
340			caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF;
341
342			memcpy(&reserved, buf, sizeof(reserved));
343			enable = reserved & STATE_BIT;
344			if (enable)
345				nbr->flags |= F_NBR_CAP_UNOTIF;
346			else
347				nbr->flags &= ~F_NBR_CAP_UNOTIF;
348
349			log_debug("%s: lsr-id %s %s the Unrecognized "
350			    "Notification capability", __func__,
351			    inet_ntoa(nbr->id), (enable) ? "announced" :
352			    "withdrew");
353			break;
354		case TLV_TYPE_DYNAMIC_CAP:
355			/*
356		 	 * RFC 5561 - Section 9:
357			 * "An LDP speaker that receives a Capability message
358			 * from a peer that includes the Dynamic Capability
359			 * Announcement Parameter SHOULD silently ignore the
360			 * parameter and process any other Capability Parameters
361			 * in the message".
362			 */
363			/* FALLTHROUGH */
364		default:
365			if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
366				send_notification_rtlvs(nbr, S_UNSSUPORTDCAP,
367				    msg.id, msg.type, tlv_type, tlv_len, buf);
368			/* ignore unknown tlv */
369			break;
370		}
371		buf += tlv_len;
372		len -= tlv_len;
373	}
374
375	nbr_fsm(nbr, NBR_EVT_PDU_RCVD);
376
377	return (0);
378}
379
380static int
381gen_init_prms_tlv(struct ibuf *buf, struct nbr *nbr)
382{
383	struct sess_prms_tlv	parms;
384
385	memset(&parms, 0, sizeof(parms));
386	parms.type = htons(TLV_TYPE_COMMONSESSION);
387	parms.length = htons(SESS_PRMS_LEN);
388	parms.proto_version = htons(LDP_VERSION);
389	parms.keepalive_time = htons(nbr_get_keepalive(nbr->af, nbr->id));
390	parms.reserved = 0;
391	parms.pvlim = 0;
392	parms.max_pdu_len = 0;
393	parms.lsr_id = nbr->id.s_addr;
394	parms.lspace_id = 0;
395
396	return (ibuf_add(buf, &parms, SESS_PRMS_SIZE));
397}
398
399static int
400gen_cap_dynamic_tlv(struct ibuf *buf)
401{
402	struct capability_tlv	cap;
403
404	memset(&cap, 0, sizeof(cap));
405	cap.type = htons(TLV_TYPE_DYNAMIC_CAP);
406	cap.length = htons(CAP_TLV_DYNAMIC_LEN);
407	/* the S-bit is always 1 for the Dynamic Capability Announcement */
408	cap.reserved = STATE_BIT;
409
410	return (ibuf_add(buf, &cap, CAP_TLV_DYNAMIC_SIZE));
411}
412
413static int
414gen_cap_twcard_tlv(struct ibuf *buf, int enable)
415{
416	struct capability_tlv	cap;
417
418	memset(&cap, 0, sizeof(cap));
419	cap.type = htons(TLV_TYPE_TWCARD_CAP);
420	cap.length = htons(CAP_TLV_TWCARD_LEN);
421	if (enable)
422		cap.reserved = STATE_BIT;
423
424	return (ibuf_add(buf, &cap, CAP_TLV_TWCARD_SIZE));
425}
426
427static int
428gen_cap_unotif_tlv(struct ibuf *buf, int enable)
429{
430	struct capability_tlv	cap;
431
432	memset(&cap, 0, sizeof(cap));
433	cap.type = htons(TLV_TYPE_UNOTIF_CAP);
434	cap.length = htons(CAP_TLV_UNOTIF_LEN);
435	if (enable)
436		cap.reserved = STATE_BIT;
437
438	return (ibuf_add(buf, &cap, CAP_TLV_UNOTIF_SIZE));
439}
440