GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/ldpd/notification.c Lines: 0 153 0.0 %
Date: 2017-11-07 Branches: 0 72 0.0 %

Line Branch Exec Source
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
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
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
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
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
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
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
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
}