]> git.proxmox.com Git - mirror_frr.git/blob - ldpd/notification.c
Merge pull request #5681 from opensourcerouting/manpage-rename
[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 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 /*
241 * RFC 5036 - Section 3.5.1.1:
242 * "When an LSR receives a Shutdown message during session
243 * initialization, it SHOULD transmit a Shutdown message and
244 * then close the transport connection".
245 */
246 if (nbr->state != NBR_STA_OPER && nm.status_code == S_SHUTDOWN)
247 send_notification(nbr->tcp, S_SHUTDOWN,
248 msg.id, msg.type);
249
250 nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
251 return (-1);
252 }
253
254 /* lde needs to know about a few notification messages */
255 switch (nm.status_code) {
256 case S_PW_STATUS:
257 case S_ENDOFLIB:
258 ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0,
259 &nm, sizeof(nm));
260 break;
261 default:
262 break;
263 }
264
265 return (0);
266 }
267
268 int
269 gen_status_tlv(struct ibuf *buf, uint32_t status_code, uint32_t msg_id,
270 uint16_t msg_type)
271 {
272 struct status_tlv st;
273
274 memset(&st, 0, sizeof(st));
275 st.type = htons(TLV_TYPE_STATUS);
276 st.length = htons(STATUS_TLV_LEN);
277 st.status_code = htonl(status_code);
278 /*
279 * For convenience, msg_id and msg_type are already in network
280 * byte order.
281 */
282 st.msg_id = msg_id;
283 st.msg_type = msg_type;
284
285 return (ibuf_add(buf, &st, STATUS_SIZE));
286 }
287
288 static int
289 gen_returned_tlvs(struct ibuf *buf, uint16_t type, uint16_t length,
290 char *tlv_data)
291 {
292 struct tlv rtlvs;
293 struct tlv tlv;
294 int err;
295
296 rtlvs.type = htons(TLV_TYPE_RETURNED_TLVS);
297 rtlvs.length = htons(length + TLV_HDR_SIZE);
298 tlv.type = htons(type);
299 tlv.length = htons(length);
300
301 err = ibuf_add(buf, &rtlvs, sizeof(rtlvs));
302 err |= ibuf_add(buf, &tlv, sizeof(tlv));
303 err |= ibuf_add(buf, tlv_data, length);
304
305 return (err);
306 }
307
308 void
309 log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm)
310 {
311 if (nm->status_code & STATUS_FATAL) {
312 debug_msg(out, "notification: lsr-id %s, status %s "
313 "(fatal error)", inet_ntoa(nbr->id),
314 status_code_name(nm->status_code));
315 return;
316 }
317
318 debug_msg(out, "notification: lsr-id %s, status %s",
319 inet_ntoa(nbr->id), status_code_name(nm->status_code));
320 if (nm->flags & F_NOTIF_FEC)
321 debug_msg(out, "notification: fec %s", log_map(&nm->fec));
322 if (nm->flags & F_NOTIF_PW_STATUS)
323 debug_msg(out, "notification: pw-status %s",
324 (nm->pw_status) ? "not forwarding" : "forwarding");
325 }