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