xref: /openbsd-src/usr.sbin/ldpd/notification.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: notification.c,v 1.41 2016/09/02 17:08:02 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 "ldp.h"
25 #include "log.h"
26 #include "ldpe.h"
27 
28 void
29 send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm)
30 {
31 	struct ibuf	*buf;
32 	uint16_t	 size;
33 	int		 err = 0;
34 
35 	/* calculate size */
36 	size = LDP_HDR_SIZE + LDP_MSG_SIZE + STATUS_SIZE;
37 	if (nm->flags & F_NOTIF_PW_STATUS)
38 		size += PW_STATUS_TLV_SIZE;
39 	if (nm->flags & F_NOTIF_FEC) {
40 		size += TLV_HDR_SIZE;
41 		switch (nm->fec.type) {
42 		case MAP_TYPE_PWID:
43 			size += FEC_PWID_ELM_MIN_LEN;
44 			if (nm->fec.flags & F_MAP_PW_ID)
45 				size += sizeof(uint32_t);
46 			break;
47 		}
48 	}
49 
50 	if ((buf = ibuf_open(size)) == NULL)
51 		fatal(__func__);
52 
53 	err |= gen_ldp_hdr(buf, size);
54 	size -= LDP_HDR_SIZE;
55 	err |= gen_msg_hdr(buf, MSG_TYPE_NOTIFICATION, size);
56 	err |= gen_status_tlv(buf, nm->status_code, nm->msg_id, nm->msg_type);
57 	/* optional tlvs */
58 	if (nm->flags & F_NOTIF_PW_STATUS)
59 		err |= gen_pw_status_tlv(buf, nm->pw_status);
60 	if (nm->flags & F_NOTIF_FEC)
61 		err |= gen_fec_tlv(buf, &nm->fec);
62 	if (err) {
63 		ibuf_free(buf);
64 		return;
65 	}
66 
67 	if (tcp->nbr)
68 		log_debug("msg-out: notification: lsr-id %s, status %s%s",
69 		    inet_ntoa(tcp->nbr->id), status_code_name(nm->status_code),
70 		    (nm->status_code & STATUS_FATAL) ? " (fatal)" : "");
71 
72 	evbuf_enqueue(&tcp->wbuf, buf);
73 }
74 
75 /* send a notification without optional tlvs */
76 void
77 send_notification(uint32_t status_code, struct tcp_conn *tcp, uint32_t msg_id,
78     uint16_t msg_type)
79 {
80 	struct notify_msg	 nm;
81 
82 	memset(&nm, 0, sizeof(nm));
83 	nm.status_code = status_code;
84 	nm.msg_id = msg_id;
85 	nm.msg_type = msg_type;
86 
87 	send_notification_full(tcp, &nm);
88 }
89 
90 void
91 send_notification_nbr(struct nbr *nbr, uint32_t status_code, uint32_t msg_id,
92     uint16_t msg_type)
93 {
94 	send_notification(status_code, nbr->tcp, msg_id, msg_type);
95 	nbr_fsm(nbr, NBR_EVT_PDU_SENT);
96 }
97 
98 int
99 recv_notification(struct nbr *nbr, char *buf, uint16_t len)
100 {
101 	struct ldp_msg		msg;
102 	struct status_tlv	st;
103 	struct notify_msg	nm;
104 	int			tlen;
105 
106 	memcpy(&msg, buf, sizeof(msg));
107 	buf += LDP_MSG_SIZE;
108 	len -= LDP_MSG_SIZE;
109 
110 	if (len < STATUS_SIZE) {
111 		session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
112 		return (-1);
113 	}
114 	memcpy(&st, buf, sizeof(st));
115 
116 	if (ntohs(st.length) > STATUS_SIZE - TLV_HDR_SIZE ||
117 	    ntohs(st.length) > len - TLV_HDR_SIZE) {
118 		session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
119 		return (-1);
120 	}
121 	buf += STATUS_SIZE;
122 	len -= STATUS_SIZE;
123 
124 	memset(&nm, 0, sizeof(nm));
125 	nm.status_code = ntohl(st.status_code);
126 
127 	/* Optional Parameters */
128 	while (len > 0) {
129 		struct tlv 	tlv;
130 		uint16_t	tlv_len;
131 
132 		if (len < sizeof(tlv)) {
133 			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
134 			return (-1);
135 		}
136 
137 		memcpy(&tlv, buf, TLV_HDR_SIZE);
138 		tlv_len = ntohs(tlv.length);
139 		if (tlv_len + TLV_HDR_SIZE > len) {
140 			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
141 			return (-1);
142 		}
143 		buf += TLV_HDR_SIZE;
144 		len -= TLV_HDR_SIZE;
145 
146 		switch (ntohs(tlv.type)) {
147 		case TLV_TYPE_EXTSTATUS:
148 		case TLV_TYPE_RETURNEDPDU:
149 		case TLV_TYPE_RETURNEDMSG:
150 			/* TODO is there any use for this? */
151 			break;
152 		case TLV_TYPE_PW_STATUS:
153 			if (tlv_len != 4) {
154 				session_shutdown(nbr, S_BAD_TLV_LEN,
155 				    msg.id, msg.type);
156 				return (-1);
157 			}
158 
159 			nm.pw_status = ntohl(*(uint32_t *)buf);
160 			nm.flags |= F_NOTIF_PW_STATUS;
161 			break;
162 		case TLV_TYPE_FEC:
163 			if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf,
164 			    tlv_len, &nm.fec)) == -1)
165 				return (-1);
166 			/* allow only one fec element */
167 			if (tlen != tlv_len) {
168 				session_shutdown(nbr, S_BAD_TLV_VAL,
169 				    msg.id, msg.type);
170 				return (-1);
171 			}
172 			nm.flags |= F_NOTIF_FEC;
173 			break;
174 		default:
175 			if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
176 				send_notification_nbr(nbr, S_UNKNOWN_TLV,
177 				    msg.id, msg.type);
178 			/* ignore unknown tlv */
179 			break;
180 		}
181 		buf += tlv_len;
182 		len -= tlv_len;
183 	}
184 
185 	if (nm.status_code == S_PW_STATUS) {
186 		if (!(nm.flags & (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) {
187 			send_notification_nbr(nbr, S_MISS_MSG,
188 			    msg.id, msg.type);
189 			return (-1);
190 		}
191 
192 		switch (nm.fec.type) {
193 		case MAP_TYPE_PWID:
194 			break;
195 		default:
196 			send_notification_nbr(nbr, S_BAD_TLV_VAL,
197 			    msg.id, msg.type);
198 			return (-1);
199 		}
200 	}
201 
202 	log_warnx("msg-in: notification: lsr-id %s, status %s%s",
203 	    inet_ntoa(nbr->id), status_code_name(ntohl(st.status_code)),
204 	    (st.status_code & htonl(STATUS_FATAL)) ? " (fatal)" : "");
205 
206 	if (st.status_code & htonl(STATUS_FATAL)) {
207 		if (nbr->state == NBR_STA_OPENSENT)
208 			nbr_start_idtimer(nbr);
209 
210 		nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
211 		return (-1);
212 	}
213 
214 	if (nm.status_code == S_PW_STATUS)
215 		ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0,
216 		    &nm, sizeof(nm));
217 
218 	return (0);
219 }
220 
221 int
222 gen_status_tlv(struct ibuf *buf, uint32_t status_code, uint32_t msg_id,
223     uint16_t msg_type)
224 {
225 	struct status_tlv	st;
226 
227 	memset(&st, 0, sizeof(st));
228 	st.type = htons(TLV_TYPE_STATUS);
229 	st.length = htons(STATUS_TLV_LEN);
230 	st.status_code = htonl(status_code);
231 	/*
232 	 * For convenience, msg_id and msg_type are already in network
233 	 * byte order.
234 	 */
235 	st.msg_id = msg_id;
236 	st.msg_type = msg_type;
237 
238 	return (ibuf_add(buf, &st, STATUS_SIZE));
239 }
240