]> git.proxmox.com Git - mirror_frr.git/blob - ldpd/notification.c
Merge remote-tracking branch 'origin/cmaster' into cmaster-next
[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 void
28 send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm)
29 {
30 struct ibuf *buf;
31 uint16_t size;
32 int err = 0;
33
34 /* calculate size */
35 size = LDP_HDR_SIZE + LDP_MSG_SIZE + STATUS_SIZE;
36 if (nm->flags & F_NOTIF_PW_STATUS)
37 size += PW_STATUS_TLV_SIZE;
38 if (nm->flags & F_NOTIF_FEC) {
39 size += TLV_HDR_SIZE;
40 switch (nm->fec.type) {
41 case MAP_TYPE_PWID:
42 size += FEC_PWID_ELM_MIN_LEN;
43 if (nm->fec.flags & F_MAP_PW_ID)
44 size += sizeof(uint32_t);
45 break;
46 }
47 }
48
49 if ((buf = ibuf_open(size)) == NULL)
50 fatal(__func__);
51
52 err |= gen_ldp_hdr(buf, size);
53 size -= LDP_HDR_SIZE;
54 err |= gen_msg_hdr(buf, MSG_TYPE_NOTIFICATION, size);
55 err |= gen_status_tlv(buf, nm->status_code, nm->msg_id, nm->msg_type);
56 /* optional tlvs */
57 if (nm->flags & F_NOTIF_PW_STATUS)
58 err |= gen_pw_status_tlv(buf, nm->pw_status);
59 if (nm->flags & F_NOTIF_FEC)
60 err |= gen_fec_tlv(buf, &nm->fec);
61 if (err) {
62 ibuf_free(buf);
63 return;
64 }
65
66 if (tcp->nbr)
67 debug_msg_send("notification: lsr-id %s status %s%s",
68 inet_ntoa(tcp->nbr->id), status_code_name(nm->status_code),
69 (nm->status_code & STATUS_FATAL) ? " (fatal)" : "");
70
71 evbuf_enqueue(&tcp->wbuf, buf);
72 }
73
74 /* send a notification without optional tlvs */
75 void
76 send_notification(uint32_t status_code, struct tcp_conn *tcp, 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_nbr(struct nbr *nbr, uint32_t status_code, uint32_t msg_id,
91 uint16_t msg_type)
92 {
93 send_notification(status_code, nbr->tcp, msg_id, msg_type);
94 nbr_fsm(nbr, NBR_EVT_PDU_SENT);
95 }
96
97 int
98 recv_notification(struct nbr *nbr, char *buf, uint16_t len)
99 {
100 struct ldp_msg msg;
101 struct status_tlv st;
102 struct notify_msg nm;
103 int tlen;
104
105 memcpy(&msg, buf, sizeof(msg));
106 buf += LDP_MSG_SIZE;
107 len -= LDP_MSG_SIZE;
108
109 if (len < STATUS_SIZE) {
110 session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type);
111 return (-1);
112 }
113 memcpy(&st, buf, sizeof(st));
114
115 if (ntohs(st.length) > STATUS_SIZE - TLV_HDR_SIZE ||
116 ntohs(st.length) > len - TLV_HDR_SIZE) {
117 session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
118 return (-1);
119 }
120 buf += STATUS_SIZE;
121 len -= STATUS_SIZE;
122
123 memset(&nm, 0, sizeof(nm));
124 nm.status_code = ntohl(st.status_code);
125
126 /* Optional Parameters */
127 while (len > 0) {
128 struct tlv tlv;
129 uint16_t tlv_len;
130
131 if (len < sizeof(tlv)) {
132 session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
133 return (-1);
134 }
135
136 memcpy(&tlv, buf, TLV_HDR_SIZE);
137 tlv_len = ntohs(tlv.length);
138 if (tlv_len + TLV_HDR_SIZE > len) {
139 session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
140 return (-1);
141 }
142 buf += TLV_HDR_SIZE;
143 len -= TLV_HDR_SIZE;
144
145 switch (ntohs(tlv.type)) {
146 case TLV_TYPE_EXTSTATUS:
147 case TLV_TYPE_RETURNEDPDU:
148 case TLV_TYPE_RETURNEDMSG:
149 /* TODO is there any use for this? */
150 break;
151 case TLV_TYPE_PW_STATUS:
152 if (tlv_len != 4) {
153 session_shutdown(nbr, S_BAD_TLV_LEN,
154 msg.id, msg.type);
155 return (-1);
156 }
157
158 nm.pw_status = ntohl(*(uint32_t *)buf);
159 nm.flags |= F_NOTIF_PW_STATUS;
160 break;
161 case TLV_TYPE_FEC:
162 if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf,
163 tlv_len, &nm.fec)) == -1)
164 return (-1);
165 /* allow only one fec element */
166 if (tlen != tlv_len) {
167 session_shutdown(nbr, S_BAD_TLV_VAL,
168 msg.id, msg.type);
169 return (-1);
170 }
171 nm.flags |= F_NOTIF_FEC;
172 break;
173 default:
174 if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
175 send_notification_nbr(nbr, S_UNKNOWN_TLV,
176 msg.id, msg.type);
177 /* ignore unknown tlv */
178 break;
179 }
180 buf += tlv_len;
181 len -= tlv_len;
182 }
183
184 if (nm.status_code == S_PW_STATUS) {
185 if (!(nm.flags & (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) {
186 send_notification_nbr(nbr, S_MISS_MSG,
187 msg.id, msg.type);
188 return (-1);
189 }
190
191 switch (nm.fec.type) {
192 case MAP_TYPE_PWID:
193 break;
194 default:
195 send_notification_nbr(nbr, S_BAD_TLV_VAL,
196 msg.id, msg.type);
197 return (-1);
198 }
199 }
200
201 debug_msg_recv("notification: lsr-id %s: %s%s", inet_ntoa(nbr->id),
202 status_code_name(ntohl(st.status_code)),
203 (st.status_code & htonl(STATUS_FATAL)) ? " (fatal)" : "");
204
205 if (st.status_code & htonl(STATUS_FATAL)) {
206 if (nbr->state == NBR_STA_OPENSENT)
207 nbr_start_idtimer(nbr);
208
209 nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION);
210 return (-1);
211 }
212
213 if (nm.status_code == S_PW_STATUS)
214 ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0,
215 &nm, sizeof(nm));
216
217 return (0);
218 }
219
220 int
221 gen_status_tlv(struct ibuf *buf, uint32_t status_code, uint32_t msg_id,
222 uint16_t msg_type)
223 {
224 struct status_tlv st;
225
226 memset(&st, 0, sizeof(st));
227 st.type = htons(TLV_TYPE_STATUS);
228 st.length = htons(STATUS_TLV_LEN);
229 st.status_code = htonl(status_code);
230 /*
231 * For convenience, msg_id and msg_type are already in network
232 * byte order.
233 */
234 st.msg_id = msg_id;
235 st.msg_type = msg_type;
236
237 return (ibuf_add(buf, &st, STATUS_SIZE));
238 }