xref: /openbsd/usr.sbin/ldpd/notification.c (revision 9246985a)
1 /*	$OpenBSD: notification.c,v 1.46 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 "ldp.h"
25 #include "log.h"
26 #include "ldpe.h"
27 
28 static int	 gen_returned_tlvs(struct ibuf *, uint16_t, uint16_t, char *);
29 static void	 log_msg_notification(int, struct nbr *, struct notify_msg *);
30 
31 void
send_notification_full(struct tcp_conn * tcp,struct notify_msg * nm)32 send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm)
33 {
34 	struct ibuf	*buf;
35 	uint16_t	 size;
36 	int		 err = 0;
37 
38 	/* calculate size */
39 	size = LDP_HDR_SIZE + LDP_MSG_SIZE + STATUS_SIZE;
40 	if (nm->flags & F_NOTIF_PW_STATUS)
41 		size += PW_STATUS_TLV_SIZE;
42 	if (nm->flags & F_NOTIF_FEC)
43 		size += len_fec_tlv(&nm->fec);
44 	if (nm->flags & F_NOTIF_RETURNED_TLVS)
45 		size += TLV_HDR_SIZE * 2 + nm->rtlvs.length;
46 
47 	if ((buf = ibuf_open(size)) == NULL)
48 		fatal(__func__);
49 
50 	err |= gen_ldp_hdr(buf, size);
51 	size -= LDP_HDR_SIZE;
52 	err |= gen_msg_hdr(buf, MSG_TYPE_NOTIFICATION, size);
53 	err |= gen_status_tlv(buf, nm->status_code, nm->msg_id, nm->msg_type);
54 	/* optional tlvs */
55 	if (nm->flags & F_NOTIF_PW_STATUS)
56 		err |= gen_pw_status_tlv(buf, nm->pw_status);
57 	if (nm->flags & F_NOTIF_FEC)
58 		err |= gen_fec_tlv(buf, &nm->fec);
59 	if (nm->flags & F_NOTIF_RETURNED_TLVS)
60 		err |= gen_returned_tlvs(buf, nm->rtlvs.type, nm->rtlvs.length,
61 		    nm->rtlvs.data);
62 	if (err) {
63 		ibuf_free(buf);
64 		return;
65 	}
66 
67 	if (tcp->nbr) {
68 		log_msg_notification(1, tcp->nbr, nm);
69 		nbr_fsm(tcp->nbr, NBR_EVT_PDU_SENT);
70 	}
71 
72 	evbuf_enqueue(&tcp->wbuf, buf);
73 }
74 
75 /* send a notification without optional tlvs */
76 void
send_notification(struct tcp_conn * tcp,uint32_t status_code,uint32_t msg_id,uint16_t msg_type)77 send_notification(struct tcp_conn *tcp, uint32_t status_code, 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
send_notification_rtlvs(struct nbr * nbr,uint32_t status_code,uint32_t msg_id,uint16_t msg_type,uint16_t tlv_type,uint16_t tlv_len,char * tlv_data)91 send_notification_rtlvs(struct nbr *nbr, uint32_t status_code, uint32_t msg_id,
92     uint16_t msg_type, uint16_t tlv_type, uint16_t tlv_len, char *tlv_data)
93 {
94 	struct notify_msg	 nm;
95 
96 	memset(&nm, 0, sizeof(nm));
97 	nm.status_code = status_code;
98 	nm.msg_id = msg_id;
99 	nm.msg_type = msg_type;
100 	/* do not append the given TLV if it's too big (shouldn't happen) */
101 	if (tlv_len < 1024) {
102 		nm.rtlvs.type = tlv_type;
103 		nm.rtlvs.length = tlv_len;
104 		nm.rtlvs.data = tlv_data;
105 		nm.flags |= F_NOTIF_RETURNED_TLVS;
106 	}
107 
108 	send_notification_full(nbr->tcp, &nm);
109 }
110 
111 int
recv_notification(struct nbr * nbr,char * buf,uint16_t len)112 recv_notification(struct nbr *nbr, char *buf, uint16_t len)
113 {
114 	struct ldp_msg		msg;
115 	struct status_tlv	st;
116 	struct notify_msg	nm;
117 	int			tlen;
118 
119 	memcpy(&msg, buf, sizeof(msg));
120 	buf += LDP_MSG_SIZE;
121 	len -= LDP_MSG_SIZE;
122 
123 	if (len < STATUS_SIZE) {
124 		session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
125 		return (-1);
126 	}
127 	memcpy(&st, buf, sizeof(st));
128 
129 	if (ntohs(st.length) > STATUS_SIZE - TLV_HDR_SIZE ||
130 	    ntohs(st.length) > len - TLV_HDR_SIZE) {
131 		session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
132 		return (-1);
133 	}
134 	buf += STATUS_SIZE;
135 	len -= STATUS_SIZE;
136 
137 	memset(&nm, 0, sizeof(nm));
138 	nm.status_code = ntohl(st.status_code);
139 
140 	/* Optional Parameters */
141 	while (len > 0) {
142 		struct tlv 	tlv;
143 		uint16_t	tlv_type;
144 		uint16_t	tlv_len;
145 
146 		if (len < sizeof(tlv)) {
147 			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
148 			return (-1);
149 		}
150 
151 		memcpy(&tlv, buf, TLV_HDR_SIZE);
152 		tlv_type = ntohs(tlv.type);
153 		tlv_len = ntohs(tlv.length);
154 		if (tlv_len + TLV_HDR_SIZE > len) {
155 			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
156 			return (-1);
157 		}
158 		buf += TLV_HDR_SIZE;
159 		len -= TLV_HDR_SIZE;
160 
161 		switch (tlv_type) {
162 		case TLV_TYPE_EXTSTATUS:
163 		case TLV_TYPE_RETURNEDPDU:
164 		case TLV_TYPE_RETURNEDMSG:
165 			/* TODO is there any use for this? */
166 			break;
167 		case TLV_TYPE_PW_STATUS:
168 			if (tlv_len != 4) {
169 				session_shutdown(nbr, S_BAD_TLV_LEN,
170 				    msg.id, msg.type);
171 				return (-1);
172 			}
173 
174 			nm.pw_status = ntohl(*(uint32_t *)buf);
175 			nm.flags |= F_NOTIF_PW_STATUS;
176 			break;
177 		case TLV_TYPE_FEC:
178 			if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf,
179 			    tlv_len, &nm.fec)) == -1)
180 				return (-1);
181 			/* allow only one fec element */
182 			if (tlen != tlv_len) {
183 				session_shutdown(nbr, S_BAD_TLV_VAL,
184 				    msg.id, msg.type);
185 				return (-1);
186 			}
187 			nm.flags |= F_NOTIF_FEC;
188 			break;
189 		default:
190 			if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
191 				send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
192 				    msg.id, msg.type, tlv_type, tlv_len, buf);
193 			/* ignore unknown tlv */
194 			break;
195 		}
196 		buf += tlv_len;
197 		len -= tlv_len;
198 	}
199 
200 	/* sanity checks */
201 	switch (nm.status_code) {
202 	case S_PW_STATUS:
203 		if (!(nm.flags & (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) {
204 			send_notification(nbr->tcp, S_MISS_MSG,
205 			    msg.id, msg.type);
206 			return (-1);
207 		}
208 
209 		switch (nm.fec.type) {
210 		case MAP_TYPE_PWID:
211 			break;
212 		default:
213 			send_notification(nbr->tcp, S_BAD_TLV_VAL,
214 			    msg.id, msg.type);
215 			return (-1);
216 		}
217 		break;
218 	case S_ENDOFLIB:
219 		if (!(nm.flags & F_NOTIF_FEC)) {
220 			send_notification(nbr->tcp, S_MISS_MSG,
221 			    msg.id, msg.type);
222 			return (-1);
223 		}
224 		if (nm.fec.type != MAP_TYPE_TYPED_WCARD) {
225 			send_notification(nbr->tcp, S_BAD_TLV_VAL,
226 			    msg.id, msg.type);
227 			return (-1);
228 		}
229 		break;
230 	default:
231 		break;
232 	}
233 
234 	log_msg_notification(0, nbr, &nm);
235 
236 	if (st.status_code & htonl(STATUS_FATAL)) {
237 		if (nbr->state == NBR_STA_OPENSENT)
238 			nbr_start_idtimer(nbr);
239 
240 		nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
241 		return (-1);
242 	}
243 
244 	/* lde needs to know about a few notification messages */
245 	switch (nm.status_code) {
246 	case S_PW_STATUS:
247 	case S_ENDOFLIB:
248 		ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0,
249 		    &nm, sizeof(nm));
250 		break;
251 	default:
252 		break;
253 	}
254 
255 	return (0);
256 }
257 
258 int
gen_status_tlv(struct ibuf * buf,uint32_t status_code,uint32_t msg_id,uint16_t msg_type)259 gen_status_tlv(struct ibuf *buf, uint32_t status_code, uint32_t msg_id,
260     uint16_t msg_type)
261 {
262 	struct status_tlv	st;
263 
264 	memset(&st, 0, sizeof(st));
265 	st.type = htons(TLV_TYPE_STATUS);
266 	st.length = htons(STATUS_TLV_LEN);
267 	st.status_code = htonl(status_code);
268 	/*
269 	 * For convenience, msg_id and msg_type are already in network
270 	 * byte order.
271 	 */
272 	st.msg_id = msg_id;
273 	st.msg_type = msg_type;
274 
275 	return (ibuf_add(buf, &st, STATUS_SIZE));
276 }
277 
278 static int
gen_returned_tlvs(struct ibuf * buf,uint16_t type,uint16_t length,char * tlv_data)279 gen_returned_tlvs(struct ibuf *buf, uint16_t type, uint16_t length,
280     char *tlv_data)
281 {
282 	struct tlv	 rtlvs;
283 	struct tlv	 tlv;
284 	int		 err;
285 
286 	rtlvs.type = htons(TLV_TYPE_RETURNED_TLVS);
287 	rtlvs.length = htons(length + TLV_HDR_SIZE);
288 	tlv.type = htons(type);
289 	tlv.length = htons(length);
290 
291 	err = ibuf_add(buf, &rtlvs, sizeof(rtlvs));
292 	err |= ibuf_add(buf, &tlv, sizeof(tlv));
293 	err |= ibuf_add(buf, tlv_data, length);
294 
295 	return (err);
296 }
297 
298 void
log_msg_notification(int out,struct nbr * nbr,struct notify_msg * nm)299 log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm)
300 {
301 	const char	*dir = (out) ? "out" : "in";
302 
303 	if (nm->status_code & STATUS_FATAL) {
304 		log_warnx("msg-%s: notification: lsr-id %s, status %s "
305 		    "(fatal error)", dir, inet_ntoa(nbr->id),
306 		    status_code_name(nm->status_code));
307 		return;
308 	}
309 
310 	log_debug("msg-%s: notification: lsr-id %s, status %s", dir,
311 	    inet_ntoa(nbr->id), status_code_name(nm->status_code));
312 	if (nm->flags & F_NOTIF_FEC)
313 		log_debug("msg-%s: notification:   fec %s", dir,
314 		    log_map(&nm->fec));
315 	if (nm->flags & F_NOTIF_PW_STATUS)
316 		log_debug("msg-%s: notification:   pw-status %s", dir,
317 		    (nm->pw_status) ? "not forwarding" : "forwarding");
318 }
319