]> git.proxmox.com Git - mirror_frr.git/blob - ldpd/notification.c
Merge pull request #8252 from SaiGomathiN/8249
[mirror_frr.git] / ldpd / notification.c
1 /* $OpenBSD$ */
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 <zebra.h>
20
21 #include "ldpd.h"
22 #include "ldp.h"
23 #include "log.h"
24 #include "ldpe.h"
25 #include "ldp_debug.h"
26
27 static int gen_returned_tlvs(struct ibuf *, uint16_t, uint16_t, char *);
28 static void log_msg_notification(int, struct nbr *, struct notify_msg *);
29
30 void
31 send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm)
32 {
33 struct ibuf *buf;
34 uint16_t size;
35 int err = 0;
36
37 /* calculate size */
38 size = LDP_HDR_SIZE + LDP_MSG_SIZE + STATUS_SIZE;
39 if (nm->flags & F_NOTIF_PW_STATUS)
40 size += PW_STATUS_TLV_SIZE;
41 if (nm->flags & F_NOTIF_FEC)
42 size += len_fec_tlv(&nm->fec);
43 if (nm->flags & F_NOTIF_RETURNED_TLVS)
44 size += TLV_HDR_SIZE * 2 + nm->rtlvs.length;
45
46 if ((buf = ibuf_open(size)) == NULL)
47 fatal(__func__);
48
49 err |= gen_ldp_hdr(buf, size);
50 size -= LDP_HDR_SIZE;
51 err |= gen_msg_hdr(buf, MSG_TYPE_NOTIFICATION, size);
52 err |= gen_status_tlv(buf, nm->status_code, nm->msg_id, nm->msg_type);
53 /* optional tlvs */
54 if (nm->flags & F_NOTIF_PW_STATUS)
55 err |= gen_pw_status_tlv(buf, nm->pw_status);
56 if (nm->flags & F_NOTIF_FEC)
57 err |= gen_fec_tlv(buf, &nm->fec);
58 if (nm->flags & F_NOTIF_RETURNED_TLVS)
59 err |= gen_returned_tlvs(buf, nm->rtlvs.type, nm->rtlvs.length,
60 nm->rtlvs.data);
61 if (err) {
62 ibuf_free(buf);
63 return;
64 }
65
66 if (tcp->nbr) {
67 log_msg_notification(1, tcp->nbr, nm);
68 nbr_fsm(tcp->nbr, NBR_EVT_PDU_SENT);
69 tcp->nbr->stats.notif_sent++;
70 }
71
72 /* update SNMP session counters */
73 switch (nm->status_code) {
74 case S_NO_HELLO:
75 leconf->stats.session_rejects_hello++;
76 break;
77 case S_BAD_LDP_ID:
78 leconf->stats.bad_ldp_id++;
79 break;
80 case S_BAD_PDU_LEN:
81 leconf->stats.bad_pdu_len++;
82 break;
83 case S_BAD_MSG_LEN:
84 leconf->stats.bad_msg_len++;
85 break;
86 case S_BAD_TLV_LEN:
87 leconf->stats.bad_tlv_len++;
88 break;
89 case S_BAD_TLV_VAL:
90 leconf->stats.malformed_tlv++;
91 break;
92 case S_KEEPALIVE_TMR:
93 leconf->stats.keepalive_timer_exp++;
94 break;
95 case S_SHUTDOWN:
96 leconf->stats.shutdown_send_notify++;
97 break;
98 default:
99 break;
100 }
101
102 evbuf_enqueue(&tcp->wbuf, buf);
103 }
104
105 /* send a notification without optional tlvs */
106 void
107 send_notification(struct tcp_conn *tcp, uint32_t status_code, uint32_t msg_id,
108 uint16_t msg_type)
109 {
110 struct notify_msg nm;
111
112 memset(&nm, 0, sizeof(nm));
113 nm.status_code = status_code;
114 nm.msg_id = msg_id;
115 nm.msg_type = msg_type;
116
117 send_notification_full(tcp, &nm);
118 }
119
120 void
121 send_notification_rtlvs(struct nbr *nbr, uint32_t status_code, uint32_t msg_id,
122 uint16_t msg_type, uint16_t tlv_type, uint16_t tlv_len, char *tlv_data)
123 {
124 struct notify_msg nm;
125
126 memset(&nm, 0, sizeof(nm));
127 nm.status_code = status_code;
128 nm.msg_id = msg_id;
129 nm.msg_type = msg_type;
130 /* do not append the given TLV if it's too big (shouldn't happen) */
131 if (tlv_len < 1024) {
132 nm.rtlvs.type = tlv_type;
133 nm.rtlvs.length = tlv_len;
134 nm.rtlvs.data = tlv_data;
135 nm.flags |= F_NOTIF_RETURNED_TLVS;
136 }
137
138 send_notification_full(nbr->tcp, &nm);
139 }
140
141 int
142 recv_notification(struct nbr *nbr, char *buf, uint16_t len)
143 {
144 struct ldp_msg msg;
145 struct status_tlv st;
146 struct notify_msg nm;
147 int tlen;
148
149 memcpy(&msg, buf, sizeof(msg));
150 buf += LDP_MSG_SIZE;
151 len -= LDP_MSG_SIZE;
152
153 if (len < STATUS_SIZE) {
154 session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
155 leconf->stats.bad_msg_len++;
156 return (-1);
157 }
158 memcpy(&st, buf, sizeof(st));
159
160 if (ntohs(st.length) > STATUS_SIZE - TLV_HDR_SIZE ||
161 ntohs(st.length) > len - TLV_HDR_SIZE) {
162 session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
163 leconf->stats.bad_tlv_len++;
164 return (-1);
165 }
166 buf += STATUS_SIZE;
167 len -= STATUS_SIZE;
168
169 memset(&nm, 0, sizeof(nm));
170 nm.status_code = ntohl(st.status_code);
171
172 /* Optional Parameters */
173 while (len > 0) {
174 struct tlv tlv;
175 uint16_t tlv_type;
176 uint16_t tlv_len;
177
178 if (len < sizeof(tlv)) {
179 session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
180 leconf->stats.bad_tlv_len++;
181 return (-1);
182 }
183
184 memcpy(&tlv, buf, TLV_HDR_SIZE);
185 tlv_type = ntohs(tlv.type);
186 tlv_len = ntohs(tlv.length);
187 if (tlv_len + TLV_HDR_SIZE > len) {
188 session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
189 leconf->stats.bad_tlv_len++;
190 return (-1);
191 }
192 buf += TLV_HDR_SIZE;
193 len -= TLV_HDR_SIZE;
194
195 switch (tlv_type) {
196 case TLV_TYPE_EXTSTATUS:
197 case TLV_TYPE_RETURNEDPDU:
198 case TLV_TYPE_RETURNEDMSG:
199 /* TODO is there any use for this? */
200 break;
201 case TLV_TYPE_PW_STATUS:
202 if (tlv_len != 4) {
203 session_shutdown(nbr, S_BAD_TLV_LEN,
204 msg.id, msg.type);
205 return (-1);
206 }
207
208 nm.pw_status = ntohl(*(uint32_t *)buf);
209 nm.flags |= F_NOTIF_PW_STATUS;
210 break;
211 case TLV_TYPE_FEC:
212 if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf,
213 tlv_len, &nm.fec)) == -1)
214 return (-1);
215 /* allow only one fec element */
216 if (tlen != tlv_len) {
217 session_shutdown(nbr, S_BAD_TLV_VAL,
218 msg.id, msg.type);
219 leconf->stats.bad_tlv_len++;
220 return (-1);
221 }
222 nm.flags |= F_NOTIF_FEC;
223 break;
224 default:
225 if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) {
226 nbr->stats.unknown_tlv++;
227 send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
228 msg.id, msg.type, tlv_type, tlv_len, buf);
229 }
230 /* ignore unknown tlv */
231 break;
232 }
233 buf += tlv_len;
234 len -= tlv_len;
235 }
236
237 /* sanity checks */
238 switch (nm.status_code) {
239 case S_PW_STATUS:
240 if (!(nm.flags & (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) {
241 send_notification(nbr->tcp, S_MISS_MSG,
242 msg.id, msg.type);
243 return (-1);
244 }
245
246 switch (nm.fec.type) {
247 case MAP_TYPE_PWID:
248 break;
249 default:
250 send_notification(nbr->tcp, S_BAD_TLV_VAL,
251 msg.id, msg.type);
252 return (-1);
253 }
254 break;
255 case S_ENDOFLIB:
256 if (!(nm.flags & F_NOTIF_FEC)) {
257 send_notification(nbr->tcp, S_MISS_MSG,
258 msg.id, msg.type);
259 return (-1);
260 }
261 if (nm.fec.type != MAP_TYPE_TYPED_WCARD) {
262 send_notification(nbr->tcp, S_BAD_TLV_VAL,
263 msg.id, msg.type);
264 return (-1);
265 }
266 break;
267 default:
268 break;
269 }
270
271 log_msg_notification(0, nbr, &nm);
272
273 if (st.status_code & htonl(STATUS_FATAL)) {
274 if (nbr->state == NBR_STA_OPENSENT)
275 nbr_start_idtimer(nbr);
276
277 /*
278 * RFC 5036 - Section 3.5.1.1:
279 * "When an LSR receives a Shutdown message during session
280 * initialization, it SHOULD transmit a Shutdown message and
281 * then close the transport connection".
282 */
283 if (nbr->state != NBR_STA_OPER &&
284 nm.status_code == S_SHUTDOWN) {
285 leconf->stats.session_attempts++;
286 send_notification(nbr->tcp, S_SHUTDOWN,
287 msg.id, msg.type);
288 }
289
290 leconf->stats.shutdown_rcv_notify++;
291 nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
292 return (-1);
293 }
294
295 /* lde needs to know about a few notification messages
296 * and update SNMP session counters
297 */
298 switch (nm.status_code) {
299 case S_PW_STATUS:
300 case S_ENDOFLIB:
301 ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0,
302 &nm, sizeof(nm));
303 break;
304 case S_NO_HELLO:
305 leconf->stats.session_rejects_hello++;
306 break;
307 case S_PARM_ADV_MODE:
308 leconf->stats.session_rejects_ad++;
309 break;
310 case S_MAX_PDU_LEN:
311 leconf->stats.session_rejects_max_pdu++;
312 break;
313 case S_PARM_L_RANGE:
314 leconf->stats.session_rejects_lr++;
315 break;
316 case S_BAD_LDP_ID:
317 leconf->stats.bad_ldp_id++;
318 break;
319 case S_BAD_PDU_LEN:
320 leconf->stats.bad_pdu_len++;
321 break;
322 case S_BAD_MSG_LEN:
323 leconf->stats.bad_msg_len++;
324 break;
325 case S_BAD_TLV_LEN:
326 leconf->stats.bad_tlv_len++;
327 break;
328 case S_BAD_TLV_VAL:
329 leconf->stats.malformed_tlv++;
330 break;
331 case S_SHUTDOWN:
332 leconf->stats.shutdown_rcv_notify++;
333 break;
334 default:
335 break;
336 }
337
338 return (0);
339 }
340
341 int
342 gen_status_tlv(struct ibuf *buf, uint32_t status_code, uint32_t msg_id,
343 uint16_t msg_type)
344 {
345 struct status_tlv st;
346
347 memset(&st, 0, sizeof(st));
348 st.type = htons(TLV_TYPE_STATUS);
349 st.length = htons(STATUS_TLV_LEN);
350 st.status_code = htonl(status_code);
351 /*
352 * For convenience, msg_id and msg_type are already in network
353 * byte order.
354 */
355 st.msg_id = msg_id;
356 st.msg_type = msg_type;
357
358 return (ibuf_add(buf, &st, STATUS_SIZE));
359 }
360
361 static int
362 gen_returned_tlvs(struct ibuf *buf, uint16_t type, uint16_t length,
363 char *tlv_data)
364 {
365 struct tlv rtlvs;
366 struct tlv tlv;
367 int err;
368
369 rtlvs.type = htons(TLV_TYPE_RETURNED_TLVS);
370 rtlvs.length = htons(length + TLV_HDR_SIZE);
371 tlv.type = htons(type);
372 tlv.length = htons(length);
373
374 err = ibuf_add(buf, &rtlvs, sizeof(rtlvs));
375 err |= ibuf_add(buf, &tlv, sizeof(tlv));
376 err |= ibuf_add(buf, tlv_data, length);
377
378 return (err);
379 }
380
381 void
382 log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm)
383 {
384 if (nm->status_code & STATUS_FATAL) {
385 debug_msg(out, "notification: lsr-id %pI4, status %s (fatal error)", &nbr->id,
386 status_code_name(nm->status_code));
387 return;
388 }
389
390 debug_msg(out, "notification: lsr-id %pI4, status %s",
391 &nbr->id, status_code_name(nm->status_code));
392 if (nm->flags & F_NOTIF_FEC)
393 debug_msg(out, "notification: fec %s", log_map(&nm->fec));
394 if (nm->flags & F_NOTIF_PW_STATUS)
395 debug_msg(out, "notification: pw-status %s",
396 (nm->pw_status == PW_FORWARDING) ? "forwarding" : "not forwarding");
397 }