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