]> git.proxmox.com Git - mirror_frr.git/blob - ldpd/notification.c
ldpd: implement RFC 5919 (LDP End-of-LIB)
[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 }
70
71 evbuf_enqueue(&tcp->wbuf, buf);
72 }
73
74 /* send a notification without optional tlvs */
75 void
76 send_notification(struct tcp_conn *tcp, uint32_t status_code, uint32_t msg_id,
77 uint16_t msg_type)
78 {
79 struct notify_msg nm;
80
81 memset(&nm, 0, sizeof(nm));
82 nm.status_code = status_code;
83 nm.msg_id = msg_id;
84 nm.msg_type = msg_type;
85
86 send_notification_full(tcp, &nm);
87 }
88
89 void
90 send_notification_rtlvs(struct nbr *nbr, uint32_t status_code, uint32_t msg_id,
91 uint16_t msg_type, uint16_t tlv_type, uint16_t tlv_len, char *tlv_data)
92 {
93 struct notify_msg nm;
94
95 memset(&nm, 0, sizeof(nm));
96 nm.status_code = status_code;
97 nm.msg_id = msg_id;
98 nm.msg_type = msg_type;
99 /* do not append the given TLV if it's too big (shouldn't happen) */
100 if (tlv_len < 1024) {
101 nm.rtlvs.type = tlv_type;
102 nm.rtlvs.length = tlv_len;
103 nm.rtlvs.data = tlv_data;
104 nm.flags |= F_NOTIF_RETURNED_TLVS;
105 }
106
107 send_notification_full(nbr->tcp, &nm);
108 }
109
110 int
111 recv_notification(struct nbr *nbr, char *buf, uint16_t len)
112 {
113 struct ldp_msg msg;
114 struct status_tlv st;
115 struct notify_msg nm;
116 int tlen;
117
118 memcpy(&msg, buf, sizeof(msg));
119 buf += LDP_MSG_SIZE;
120 len -= LDP_MSG_SIZE;
121
122 if (len < STATUS_SIZE) {
123 session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
124 return (-1);
125 }
126 memcpy(&st, buf, sizeof(st));
127
128 if (ntohs(st.length) > STATUS_SIZE - TLV_HDR_SIZE ||
129 ntohs(st.length) > len - TLV_HDR_SIZE) {
130 session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
131 return (-1);
132 }
133 buf += STATUS_SIZE;
134 len -= STATUS_SIZE;
135
136 memset(&nm, 0, sizeof(nm));
137 nm.status_code = ntohl(st.status_code);
138
139 /* Optional Parameters */
140 while (len > 0) {
141 struct tlv tlv;
142 uint16_t tlv_type;
143 uint16_t tlv_len;
144
145 if (len < sizeof(tlv)) {
146 session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
147 return (-1);
148 }
149
150 memcpy(&tlv, buf, TLV_HDR_SIZE);
151 tlv_type = ntohs(tlv.type);
152 tlv_len = ntohs(tlv.length);
153 if (tlv_len + TLV_HDR_SIZE > len) {
154 session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
155 return (-1);
156 }
157 buf += TLV_HDR_SIZE;
158 len -= TLV_HDR_SIZE;
159
160 switch (tlv_type) {
161 case TLV_TYPE_EXTSTATUS:
162 case TLV_TYPE_RETURNEDPDU:
163 case TLV_TYPE_RETURNEDMSG:
164 /* TODO is there any use for this? */
165 break;
166 case TLV_TYPE_PW_STATUS:
167 if (tlv_len != 4) {
168 session_shutdown(nbr, S_BAD_TLV_LEN,
169 msg.id, msg.type);
170 return (-1);
171 }
172
173 nm.pw_status = ntohl(*(uint32_t *)buf);
174 nm.flags |= F_NOTIF_PW_STATUS;
175 break;
176 case TLV_TYPE_FEC:
177 if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf,
178 tlv_len, &nm.fec)) == -1)
179 return (-1);
180 /* allow only one fec element */
181 if (tlen != tlv_len) {
182 session_shutdown(nbr, S_BAD_TLV_VAL,
183 msg.id, msg.type);
184 return (-1);
185 }
186 nm.flags |= F_NOTIF_FEC;
187 break;
188 default:
189 if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
190 send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
191 msg.id, msg.type, tlv_type, tlv_len, buf);
192 /* ignore unknown tlv */
193 break;
194 }
195 buf += tlv_len;
196 len -= tlv_len;
197 }
198
199 /* sanity checks */
200 switch (nm.status_code) {
201 case S_PW_STATUS:
202 if (!(nm.flags & (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) {
203 send_notification(nbr->tcp, S_MISS_MSG,
204 msg.id, msg.type);
205 return (-1);
206 }
207
208 switch (nm.fec.type) {
209 case MAP_TYPE_PWID:
210 break;
211 default:
212 send_notification(nbr->tcp, S_BAD_TLV_VAL,
213 msg.id, msg.type);
214 return (-1);
215 }
216 break;
217 case S_ENDOFLIB:
218 if (!(nm.flags & F_NOTIF_FEC)) {
219 send_notification(nbr->tcp, S_MISS_MSG,
220 msg.id, msg.type);
221 return (-1);
222 }
223 if (nm.fec.type != MAP_TYPE_TYPED_WCARD) {
224 send_notification(nbr->tcp, S_BAD_TLV_VAL,
225 msg.id, msg.type);
226 return (-1);
227 }
228 break;
229 default:
230 break;
231 }
232
233 log_msg_notification(0, nbr, &nm);
234
235 if (st.status_code & htonl(STATUS_FATAL)) {
236 if (nbr->state == NBR_STA_OPENSENT)
237 nbr_start_idtimer(nbr);
238
239 nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
240 return (-1);
241 }
242
243 /* lde needs to know about a few notification messages */
244 switch (nm.status_code) {
245 case S_PW_STATUS:
246 case S_ENDOFLIB:
247 ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0,
248 &nm, sizeof(nm));
249 break;
250 default:
251 break;
252 }
253
254 return (0);
255 }
256
257 int
258 gen_status_tlv(struct ibuf *buf, uint32_t status_code, uint32_t msg_id,
259 uint16_t msg_type)
260 {
261 struct status_tlv st;
262
263 memset(&st, 0, sizeof(st));
264 st.type = htons(TLV_TYPE_STATUS);
265 st.length = htons(STATUS_TLV_LEN);
266 st.status_code = htonl(status_code);
267 /*
268 * For convenience, msg_id and msg_type are already in network
269 * byte order.
270 */
271 st.msg_id = msg_id;
272 st.msg_type = msg_type;
273
274 return (ibuf_add(buf, &st, STATUS_SIZE));
275 }
276
277 static int
278 gen_returned_tlvs(struct ibuf *buf, uint16_t type, uint16_t length,
279 char *tlv_data)
280 {
281 struct tlv rtlvs;
282 struct tlv tlv;
283 int err;
284
285 rtlvs.type = htons(TLV_TYPE_RETURNED_TLVS);
286 rtlvs.length = htons(length + TLV_HDR_SIZE);
287 tlv.type = htons(type);
288 tlv.length = htons(length);
289
290 err = ibuf_add(buf, &rtlvs, sizeof(rtlvs));
291 err |= ibuf_add(buf, &tlv, sizeof(tlv));
292 err |= ibuf_add(buf, tlv_data, length);
293
294 return (err);
295 }
296
297 void
298 log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm)
299 {
300 if (nm->status_code & STATUS_FATAL) {
301 debug_msg(out, "notification: lsr-id %s, status %s "
302 "(fatal error)", inet_ntoa(nbr->id),
303 status_code_name(nm->status_code));
304 return;
305 }
306
307 debug_msg(out, "notification: lsr-id %s, status %s",
308 inet_ntoa(nbr->id), status_code_name(nm->status_code));
309 if (nm->flags & F_NOTIF_FEC)
310 debug_msg(out, "notification: fec %s", log_map(&nm->fec));
311 if (nm->flags & F_NOTIF_PW_STATUS)
312 debug_msg(out, "notification: pw-status %s",
313 (nm->pw_status) ? "not forwarding" : "forwarding");
314 }