1 /* $OpenBSD: notification.c,v 1.46 2017/03/04 00:15:35 renato Exp $ */ 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 <sys/types.h> 20 #include <arpa/inet.h> 21 #include <string.h> 22 23 #include "ldpd.h" 24 #include "ldp.h" 25 #include "log.h" 26 #include "ldpe.h" 27 28 static int gen_returned_tlvs(struct ibuf *, uint16_t, uint16_t, char *); 29 static void log_msg_notification(int, struct nbr *, struct notify_msg *); 30 31 void 32 send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm) 33 { 34 struct ibuf *buf; 35 uint16_t size; 36 int err = 0; 37 38 /* calculate size */ 39 size = LDP_HDR_SIZE + LDP_MSG_SIZE + STATUS_SIZE; 40 if (nm->flags & F_NOTIF_PW_STATUS) 41 size += PW_STATUS_TLV_SIZE; 42 if (nm->flags & F_NOTIF_FEC) 43 size += len_fec_tlv(&nm->fec); 44 if (nm->flags & F_NOTIF_RETURNED_TLVS) 45 size += TLV_HDR_SIZE * 2 + nm->rtlvs.length; 46 47 if ((buf = ibuf_open(size)) == NULL) 48 fatal(__func__); 49 50 err |= gen_ldp_hdr(buf, size); 51 size -= LDP_HDR_SIZE; 52 err |= gen_msg_hdr(buf, MSG_TYPE_NOTIFICATION, size); 53 err |= gen_status_tlv(buf, nm->status_code, nm->msg_id, nm->msg_type); 54 /* optional tlvs */ 55 if (nm->flags & F_NOTIF_PW_STATUS) 56 err |= gen_pw_status_tlv(buf, nm->pw_status); 57 if (nm->flags & F_NOTIF_FEC) 58 err |= gen_fec_tlv(buf, &nm->fec); 59 if (nm->flags & F_NOTIF_RETURNED_TLVS) 60 err |= gen_returned_tlvs(buf, nm->rtlvs.type, nm->rtlvs.length, 61 nm->rtlvs.data); 62 if (err) { 63 ibuf_free(buf); 64 return; 65 } 66 67 if (tcp->nbr) { 68 log_msg_notification(1, tcp->nbr, nm); 69 nbr_fsm(tcp->nbr, NBR_EVT_PDU_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 nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); 241 return (-1); 242 } 243 244 /* lde needs to know about a few notification messages */ 245 switch (nm.status_code) { 246 case S_PW_STATUS: 247 case S_ENDOFLIB: 248 ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0, 249 &nm, sizeof(nm)); 250 break; 251 default: 252 break; 253 } 254 255 return (0); 256 } 257 258 int 259 gen_status_tlv(struct ibuf *buf, uint32_t status_code, uint32_t msg_id, 260 uint16_t msg_type) 261 { 262 struct status_tlv st; 263 264 memset(&st, 0, sizeof(st)); 265 st.type = htons(TLV_TYPE_STATUS); 266 st.length = htons(STATUS_TLV_LEN); 267 st.status_code = htonl(status_code); 268 /* 269 * For convenience, msg_id and msg_type are already in network 270 * byte order. 271 */ 272 st.msg_id = msg_id; 273 st.msg_type = msg_type; 274 275 return (ibuf_add(buf, &st, STATUS_SIZE)); 276 } 277 278 static int 279 gen_returned_tlvs(struct ibuf *buf, uint16_t type, uint16_t length, 280 char *tlv_data) 281 { 282 struct tlv rtlvs; 283 struct tlv tlv; 284 int err; 285 286 rtlvs.type = htons(TLV_TYPE_RETURNED_TLVS); 287 rtlvs.length = htons(length + TLV_HDR_SIZE); 288 tlv.type = htons(type); 289 tlv.length = htons(length); 290 291 err = ibuf_add(buf, &rtlvs, sizeof(rtlvs)); 292 err |= ibuf_add(buf, &tlv, sizeof(tlv)); 293 err |= ibuf_add(buf, tlv_data, length); 294 295 return (err); 296 } 297 298 void 299 log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm) 300 { 301 const char *dir = (out) ? "out" : "in"; 302 303 if (nm->status_code & STATUS_FATAL) { 304 log_warnx("msg-%s: notification: lsr-id %s, status %s " 305 "(fatal error)", dir, inet_ntoa(nbr->id), 306 status_code_name(nm->status_code)); 307 return; 308 } 309 310 log_debug("msg-%s: notification: lsr-id %s, status %s", dir, 311 inet_ntoa(nbr->id), status_code_name(nm->status_code)); 312 if (nm->flags & F_NOTIF_FEC) 313 log_debug("msg-%s: notification: fec %s", dir, 314 log_map(&nm->fec)); 315 if (nm->flags & F_NOTIF_PW_STATUS) 316 log_debug("msg-%s: notification: pw-status %s", dir, 317 (nm->pw_status) ? "not forwarding" : "forwarding"); 318 } 319