[BACK]Return to init.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / ldpd

File: [local] / src / usr.sbin / ldpd / init.c (download)

Revision 1.37, Sat Mar 4 00:15:35 2017 UTC (7 years, 3 months ago) by renato
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7, OPENBSD_6_6_BASE, OPENBSD_6_6, OPENBSD_6_5_BASE, OPENBSD_6_5, OPENBSD_6_4_BASE, OPENBSD_6_4, OPENBSD_6_3_BASE, OPENBSD_6_3, OPENBSD_6_2_BASE, OPENBSD_6_2, OPENBSD_6_1_BASE, OPENBSD_6_1, HEAD
Changes since 1.36: +67 -2 lines

Implement RFC 5919 (LDP End-of-LIB).

/*	$OpenBSD: init.c,v 1.37 2017/03/04 00:15:35 renato Exp $ */

/*
 * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>

#include "ldpd.h"
#include "ldpe.h"
#include "log.h"

static int	gen_init_prms_tlv(struct ibuf *, struct nbr *);
static int	gen_cap_dynamic_tlv(struct ibuf *);
static int	gen_cap_twcard_tlv(struct ibuf *, int);
static int	gen_cap_unotif_tlv(struct ibuf *, int);

void
send_init(struct nbr *nbr)
{
	struct ibuf		*buf;
	uint16_t		 size;
	int			 err = 0;

	log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));

	size = LDP_HDR_SIZE + LDP_MSG_SIZE + SESS_PRMS_SIZE +
	    CAP_TLV_DYNAMIC_SIZE + CAP_TLV_TWCARD_SIZE + CAP_TLV_UNOTIF_SIZE;
	if ((buf = ibuf_open(size)) == NULL)
		fatal(__func__);

	err |= gen_ldp_hdr(buf, size);
	size -= LDP_HDR_SIZE;
	err |= gen_msg_hdr(buf, MSG_TYPE_INIT, size);
	err |= gen_init_prms_tlv(buf, nbr);
	err |= gen_cap_dynamic_tlv(buf);
	err |= gen_cap_twcard_tlv(buf, 1);
	err |= gen_cap_unotif_tlv(buf, 1);
	if (err) {
		ibuf_free(buf);
		return;
	}

	evbuf_enqueue(&nbr->tcp->wbuf, buf);
}

int
recv_init(struct nbr *nbr, char *buf, uint16_t len)
{
	struct ldp_msg		msg;
	struct sess_prms_tlv	sess;
	uint16_t		max_pdu_len;
	int			caps_rcvd = 0;

	log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));

	memcpy(&msg, buf, sizeof(msg));
	buf += LDP_MSG_SIZE;
	len -= LDP_MSG_SIZE;

	if (len < SESS_PRMS_SIZE) {
		session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
		return (-1);
	}
	memcpy(&sess, buf, sizeof(sess));
	if (ntohs(sess.length) != SESS_PRMS_LEN) {
		session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
		return (-1);
	}
	if (ntohs(sess.proto_version) != LDP_VERSION) {
		session_shutdown(nbr, S_BAD_PROTO_VER, msg.id, msg.type);
		return (-1);
	}
	if (ntohs(sess.keepalive_time) < MIN_KEEPALIVE) {
		session_shutdown(nbr, S_KEEPALIVE_BAD, msg.id, msg.type);
		return (-1);
	}
	if (sess.lsr_id != leconf->rtr_id.s_addr ||
	    ntohs(sess.lspace_id) != 0) {
		session_shutdown(nbr, S_NO_HELLO, msg.id, msg.type);
		return (-1);
	}

	buf += SESS_PRMS_SIZE;
	len -= SESS_PRMS_SIZE;

	/* Optional Parameters */
	while (len > 0) {
		struct tlv 	tlv;
		uint16_t	tlv_type;
		uint16_t	tlv_len;

		if (len < sizeof(tlv)) {
			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
			return (-1);
		}

		memcpy(&tlv, buf, TLV_HDR_SIZE);
		tlv_type = ntohs(tlv.type);
		tlv_len = ntohs(tlv.length);
		if (tlv_len + TLV_HDR_SIZE > len) {
			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
			return (-1);
		}
		buf += TLV_HDR_SIZE;
		len -= TLV_HDR_SIZE;

		/*
		 * RFC 5561 - Section 6:
		 * "The S-bit of a Capability Parameter in an Initialization
		 * message MUST be 1 and SHOULD be ignored on receipt".
		 */
		switch (tlv_type) {
		case TLV_TYPE_ATMSESSIONPAR:
			session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
			return (-1);
		case TLV_TYPE_FRSESSION:
			session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
			return (-1);
		case TLV_TYPE_DYNAMIC_CAP:
			if (tlv_len != CAP_TLV_DYNAMIC_LEN) {
				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
				    msg.type);
				return (-1);
			}

			if (caps_rcvd & F_CAP_TLV_RCVD_DYNAMIC) {
				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
				    msg.type);
				return (-1);
			}
			caps_rcvd |= F_CAP_TLV_RCVD_DYNAMIC;

			nbr->flags |= F_NBR_CAP_DYNAMIC;

			log_debug("%s: lsr-id %s announced the Dynamic "
			    "Capability Announcement capability", __func__,
			    inet_ntoa(nbr->id));
			break;
		case TLV_TYPE_TWCARD_CAP:
			if (tlv_len != CAP_TLV_TWCARD_LEN) {
				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
				    msg.type);
				return (-1);
			}

			if (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) {
				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
				    msg.type);
				return (-1);
			}
			caps_rcvd |= F_CAP_TLV_RCVD_TWCARD;

			nbr->flags |= F_NBR_CAP_TWCARD;

			log_debug("%s: lsr-id %s announced the Typed Wildcard "
			    "FEC capability", __func__, inet_ntoa(nbr->id));
			break;
		case TLV_TYPE_UNOTIF_CAP:
			if (tlv_len != CAP_TLV_UNOTIF_LEN) {
				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
				    msg.type);
				return (-1);
			}

			if (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) {
				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
				    msg.type);
				return (-1);
			}
			caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF;

			nbr->flags |= F_NBR_CAP_UNOTIF;

			log_debug("%s: lsr-id %s announced the Unrecognized "
			    "Notification capability", __func__,
			    inet_ntoa(nbr->id));
			break;
		default:
			if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
				send_notification_rtlvs(nbr, S_UNSSUPORTDCAP,
				    msg.id, msg.type, tlv_type, tlv_len, buf);
			/* ignore unknown tlv */
			break;
		}
		buf += tlv_len;
		len -= tlv_len;
	}

	nbr->keepalive = min(nbr_get_keepalive(nbr->af, nbr->id),
	    ntohs(sess.keepalive_time));

	max_pdu_len = ntohs(sess.max_pdu_len);
	/*
	 * RFC 5036 - Section 3.5.3:
	 * "A value of 255 or less specifies the default maximum length of
	 * 4096 octets".
	 */
	if (max_pdu_len <= 255)
		max_pdu_len = LDP_MAX_LEN;
	nbr->max_pdu_len = min(max_pdu_len, LDP_MAX_LEN);

	nbr_fsm(nbr, NBR_EVT_INIT_RCVD);

	return (0);
}

void
send_capability(struct nbr *nbr, uint16_t capability, int enable)
{
	struct ibuf		*buf;
	uint16_t		 size;
	int			 err = 0;

	log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));

	size = LDP_HDR_SIZE + LDP_MSG_SIZE + CAP_TLV_DYNAMIC_SIZE;
	if ((buf = ibuf_open(size)) == NULL)
		fatal(__func__);

	err |= gen_ldp_hdr(buf, size);
	size -= LDP_HDR_SIZE;
	err |= gen_msg_hdr(buf, MSG_TYPE_CAPABILITY, size);

	switch (capability) {
	case TLV_TYPE_TWCARD_CAP:
		err |= gen_cap_twcard_tlv(buf, enable);
		break;
	case TLV_TYPE_UNOTIF_CAP:
		err |= gen_cap_unotif_tlv(buf, enable);
		break;
	case TLV_TYPE_DYNAMIC_CAP:
		/*
		 * RFC 5561 - Section 9:
		 * "An LDP speaker MUST NOT include the Dynamic Capability
		 * Announcement Parameter in Capability messages sent to
		 * its peers".
		 */
		/* FALLTHROUGH */
	default:
		fatalx("send_capability: unsupported capability");
	}

	if (err) {
		ibuf_free(buf);
		return;
	}

	evbuf_enqueue(&nbr->tcp->wbuf, buf);
	nbr_fsm(nbr, NBR_EVT_PDU_SENT);
}

int
recv_capability(struct nbr *nbr, char *buf, uint16_t len)
{
	struct ldp_msg	 msg;
	int		 enable = 0;
	int		 caps_rcvd = 0;

	log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));

	memcpy(&msg, buf, sizeof(msg));
	buf += LDP_MSG_SIZE;
	len -= LDP_MSG_SIZE;

	/* Optional Parameters */
	while (len > 0) {
		struct tlv 	 tlv;
		uint16_t	 tlv_type;
		uint16_t	 tlv_len;
		uint8_t		 reserved;

		if (len < sizeof(tlv)) {
			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
			return (-1);
		}

		memcpy(&tlv, buf, TLV_HDR_SIZE);
		tlv_type = ntohs(tlv.type);
		tlv_len = ntohs(tlv.length);
		if (tlv_len + TLV_HDR_SIZE > len) {
			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
			return (-1);
		}
		buf += TLV_HDR_SIZE;
		len -= TLV_HDR_SIZE;

		switch (tlv_type) {
		case TLV_TYPE_TWCARD_CAP:
			if (tlv_len != CAP_TLV_TWCARD_LEN) {
				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
				    msg.type);
				return (-1);
			}

			if (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) {
				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
				    msg.type);
				return (-1);
			}
			caps_rcvd |= F_CAP_TLV_RCVD_TWCARD;

			memcpy(&reserved, buf, sizeof(reserved));
			enable = reserved & STATE_BIT;
			if (enable)
				nbr->flags |= F_NBR_CAP_TWCARD;
			else
				nbr->flags &= ~F_NBR_CAP_TWCARD;

			log_debug("%s: lsr-id %s %s the Typed Wildcard FEC "
			    "capability", __func__, inet_ntoa(nbr->id),
			    (enable) ? "announced" : "withdrew");
			break;
		case TLV_TYPE_UNOTIF_CAP:
			if (tlv_len != CAP_TLV_UNOTIF_LEN) {
				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
				    msg.type);
				return (-1);
			}

			if (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) {
				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
				    msg.type);
				return (-1);
			}
			caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF;

			memcpy(&reserved, buf, sizeof(reserved));
			enable = reserved & STATE_BIT;
			if (enable)
				nbr->flags |= F_NBR_CAP_UNOTIF;
			else
				nbr->flags &= ~F_NBR_CAP_UNOTIF;

			log_debug("%s: lsr-id %s %s the Unrecognized "
			    "Notification capability", __func__,
			    inet_ntoa(nbr->id), (enable) ? "announced" :
			    "withdrew");
			break;
		case TLV_TYPE_DYNAMIC_CAP:
			/*
		 	 * RFC 5561 - Section 9:
			 * "An LDP speaker that receives a Capability message
			 * from a peer that includes the Dynamic Capability
			 * Announcement Parameter SHOULD silently ignore the
			 * parameter and process any other Capability Parameters
			 * in the message".
			 */
			/* FALLTHROUGH */
		default:
			if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
				send_notification_rtlvs(nbr, S_UNSSUPORTDCAP,
				    msg.id, msg.type, tlv_type, tlv_len, buf);
			/* ignore unknown tlv */
			break;
		}
		buf += tlv_len;
		len -= tlv_len;
	}

	nbr_fsm(nbr, NBR_EVT_PDU_RCVD);

	return (0);
}

static int
gen_init_prms_tlv(struct ibuf *buf, struct nbr *nbr)
{
	struct sess_prms_tlv	parms;

	memset(&parms, 0, sizeof(parms));
	parms.type = htons(TLV_TYPE_COMMONSESSION);
	parms.length = htons(SESS_PRMS_LEN);
	parms.proto_version = htons(LDP_VERSION);
	parms.keepalive_time = htons(nbr_get_keepalive(nbr->af, nbr->id));
	parms.reserved = 0;
	parms.pvlim = 0;
	parms.max_pdu_len = 0;
	parms.lsr_id = nbr->id.s_addr;
	parms.lspace_id = 0;

	return (ibuf_add(buf, &parms, SESS_PRMS_SIZE));
}

static int
gen_cap_dynamic_tlv(struct ibuf *buf)
{
	struct capability_tlv	cap;

	memset(&cap, 0, sizeof(cap));
	cap.type = htons(TLV_TYPE_DYNAMIC_CAP);
	cap.length = htons(CAP_TLV_DYNAMIC_LEN);
	/* the S-bit is always 1 for the Dynamic Capability Announcement */
	cap.reserved = STATE_BIT;

	return (ibuf_add(buf, &cap, CAP_TLV_DYNAMIC_SIZE));
}

static int
gen_cap_twcard_tlv(struct ibuf *buf, int enable)
{
	struct capability_tlv	cap;

	memset(&cap, 0, sizeof(cap));
	cap.type = htons(TLV_TYPE_TWCARD_CAP);
	cap.length = htons(CAP_TLV_TWCARD_LEN);
	if (enable)
		cap.reserved = STATE_BIT;

	return (ibuf_add(buf, &cap, CAP_TLV_TWCARD_SIZE));
}

static int
gen_cap_unotif_tlv(struct ibuf *buf, int enable)
{
	struct capability_tlv	cap;

	memset(&cap, 0, sizeof(cap));
	cap.type = htons(TLV_TYPE_UNOTIF_CAP);
	cap.length = htons(CAP_TLV_UNOTIF_LEN);
	if (enable)
		cap.reserved = STATE_BIT;

	return (ibuf_add(buf, &cap, CAP_TLV_UNOTIF_SIZE));
}