1 /** 2 * @file 3 * SNMPv1 traps implementation. 4 */ 5 6 /* 7 * Copyright (c) 2001-2004 Swedish Institute of Computer Science. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without modification, 11 * are permitted provided that the following conditions are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright notice, 14 * this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright notice, 16 * this list of conditions and the following disclaimer in the documentation 17 * and/or other materials provided with the distribution. 18 * 3. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 24 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 26 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 30 * OF SUCH DAMAGE. 31 * 32 * This file is part of the lwIP TCP/IP stack. 33 * 34 * Author: Martin Hentschel 35 * Christiaan Simons <[email protected]> 36 * 37 */ 38 39 #include "lwip/apps/snmp_opts.h" 40 41 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ 42 43 #include <string.h> 44 45 #include "lwip/snmp.h" 46 #include "lwip/sys.h" 47 #include "lwip/apps/snmp.h" 48 #include "lwip/apps/snmp_core.h" 49 #include "lwip/prot/iana.h" 50 #include "snmp_msg.h" 51 #include "snmp_asn1.h" 52 #include "snmp_core_priv.h" 53 54 struct snmp_msg_trap { 55 /* source enterprise ID (sysObjectID) */ 56 const struct snmp_obj_id *enterprise; 57 /* source IP address, raw network order format */ 58 ip_addr_t sip; 59 /* generic trap code */ 60 u32_t gen_trap; 61 /* specific trap code */ 62 u32_t spc_trap; 63 /* timestamp */ 64 u32_t ts; 65 /* snmp_version */ 66 u32_t snmp_version; 67 68 /* output trap lengths used in ASN encoding */ 69 /* encoding pdu length */ 70 u16_t pdulen; 71 /* encoding community length */ 72 u16_t comlen; 73 /* encoding sequence length */ 74 u16_t seqlen; 75 /* encoding varbinds sequence length */ 76 u16_t vbseqlen; 77 }; 78 79 static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds); 80 static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len); 81 static err_t snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream); 82 static err_t snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds); 83 84 #define BUILD_EXEC(code) \ 85 if ((code) != ERR_OK) { \ 86 LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound trap frame!")); \ 87 return ERR_ARG; \ 88 } 89 90 /** Agent community string for sending traps */ 91 extern const char *snmp_community_trap; 92 93 void *snmp_traps_handle; 94 95 struct snmp_trap_dst { 96 /* destination IP address in network order */ 97 ip_addr_t dip; 98 /* set to 0 when disabled, >0 when enabled */ 99 u8_t enable; 100 }; 101 static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS]; 102 103 static u8_t snmp_auth_traps_enabled = 0; 104 105 /** 106 * @ingroup snmp_traps 107 * Sets enable switch for this trap destination. 108 * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 109 * @param enable switch if 0 destination is disabled >0 enabled. 110 */ 111 void 112 snmp_trap_dst_enable(u8_t dst_idx, u8_t enable) 113 { 114 LWIP_ASSERT_CORE_LOCKED(); 115 if (dst_idx < SNMP_TRAP_DESTINATIONS) { 116 trap_dst[dst_idx].enable = enable; 117 } 118 } 119 120 /** 121 * @ingroup snmp_traps 122 * Sets IPv4 address for this trap destination. 123 * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 124 * @param dst IPv4 address in host order. 125 */ 126 void 127 snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst) 128 { 129 LWIP_ASSERT_CORE_LOCKED(); 130 if (dst_idx < SNMP_TRAP_DESTINATIONS) { 131 ip_addr_set(&trap_dst[dst_idx].dip, dst); 132 } 133 } 134 135 /** 136 * @ingroup snmp_traps 137 * Enable/disable authentication traps 138 */ 139 void 140 snmp_set_auth_traps_enabled(u8_t enable) 141 { 142 snmp_auth_traps_enabled = enable; 143 } 144 145 /** 146 * @ingroup snmp_traps 147 * Get authentication traps enabled state 148 */ 149 u8_t 150 snmp_get_auth_traps_enabled(void) 151 { 152 return snmp_auth_traps_enabled; 153 } 154 155 156 /** 157 * @ingroup snmp_traps 158 * Sends a generic or enterprise specific trap message. 159 * 160 * @param eoid points to enterprise object identifier 161 * @param generic_trap is the trap code 162 * @param specific_trap used for enterprise traps when generic_trap == 6 163 * @param varbinds linked list of varbinds to be sent 164 * @return ERR_OK when success, ERR_MEM if we're out of memory 165 * 166 * @note the use of the enterprise identifier field 167 * is per RFC1215. 168 * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps 169 * and .iso.org.dod.internet.private.enterprises.yourenterprise 170 * (sysObjectID) for specific traps. 171 */ 172 err_t 173 snmp_send_trap(const struct snmp_obj_id *eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds) 174 { 175 struct snmp_msg_trap trap_msg; 176 struct snmp_trap_dst *td; 177 struct pbuf *p; 178 u16_t i, tot_len; 179 err_t err = ERR_OK; 180 181 LWIP_ASSERT_CORE_LOCKED(); 182 183 trap_msg.snmp_version = 0; 184 185 for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) { 186 if ((td->enable != 0) && !ip_addr_isany(&td->dip)) { 187 /* lookup current source address for this dst */ 188 if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg.sip)) { 189 if (eoid == NULL) { 190 trap_msg.enterprise = snmp_get_device_enterprise_oid(); 191 } else { 192 trap_msg.enterprise = eoid; 193 } 194 195 trap_msg.gen_trap = generic_trap; 196 if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) { 197 trap_msg.spc_trap = specific_trap; 198 } else { 199 trap_msg.spc_trap = 0; 200 } 201 202 MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts); 203 204 /* pass 0, calculate length fields */ 205 tot_len = snmp_trap_varbind_sum(&trap_msg, varbinds); 206 tot_len = snmp_trap_header_sum(&trap_msg, tot_len); 207 208 /* allocate pbuf(s) */ 209 p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM); 210 if (p != NULL) { 211 struct snmp_pbuf_stream pbuf_stream; 212 snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len); 213 214 /* pass 1, encode packet into the pbuf(s) */ 215 snmp_trap_header_enc(&trap_msg, &pbuf_stream); 216 snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds); 217 218 snmp_stats.outtraps++; 219 snmp_stats.outpkts++; 220 221 /** send to the TRAP destination */ 222 snmp_sendto(snmp_traps_handle, p, &td->dip, LWIP_IANA_PORT_SNMP_TRAP); 223 pbuf_free(p); 224 } else { 225 err = ERR_MEM; 226 } 227 } else { 228 /* routing error */ 229 err = ERR_RTE; 230 } 231 } 232 } 233 return err; 234 } 235 236 /** 237 * @ingroup snmp_traps 238 * Send generic SNMP trap 239 */ 240 err_t 241 snmp_send_trap_generic(s32_t generic_trap) 242 { 243 static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } }; 244 return snmp_send_trap(&oid, generic_trap, 0, NULL); 245 } 246 247 /** 248 * @ingroup snmp_traps 249 * Send specific SNMP trap with variable bindings 250 */ 251 err_t 252 snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds) 253 { 254 return snmp_send_trap(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds); 255 } 256 257 /** 258 * @ingroup snmp_traps 259 * Send coldstart trap 260 */ 261 void 262 snmp_coldstart_trap(void) 263 { 264 snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART); 265 } 266 267 /** 268 * @ingroup snmp_traps 269 * Send authentication failure trap (used internally by agent) 270 */ 271 void 272 snmp_authfail_trap(void) 273 { 274 if (snmp_auth_traps_enabled != 0) { 275 snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE); 276 } 277 } 278 279 static u16_t 280 snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds) 281 { 282 struct snmp_varbind *varbind; 283 u16_t tot_len; 284 u8_t tot_len_len; 285 286 tot_len = 0; 287 varbind = varbinds; 288 while (varbind != NULL) { 289 struct snmp_varbind_len len; 290 291 if (snmp_varbind_length(varbind, &len) == ERR_OK) { 292 tot_len += 1 + len.vb_len_len + len.vb_value_len; 293 } 294 295 varbind = varbind->next; 296 } 297 298 trap->vbseqlen = tot_len; 299 snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len); 300 tot_len += 1 + tot_len_len; 301 302 return tot_len; 303 } 304 305 /** 306 * Sums trap header field lengths from tail to head and 307 * returns trap_header_lengths for second encoding pass. 308 * 309 * @param trap Trap message 310 * @param vb_len varbind-list length 311 * @return the required length for encoding the trap header 312 */ 313 static u16_t 314 snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len) 315 { 316 u16_t tot_len; 317 u16_t len; 318 u8_t lenlen; 319 320 tot_len = vb_len; 321 322 snmp_asn1_enc_u32t_cnt(trap->ts, &len); 323 snmp_asn1_enc_length_cnt(len, &lenlen); 324 tot_len += 1 + len + lenlen; 325 326 snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len); 327 snmp_asn1_enc_length_cnt(len, &lenlen); 328 tot_len += 1 + len + lenlen; 329 330 snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len); 331 snmp_asn1_enc_length_cnt(len, &lenlen); 332 tot_len += 1 + len + lenlen; 333 334 if (IP_IS_V6_VAL(trap->sip)) { 335 #if LWIP_IPV6 336 len = sizeof(ip_2_ip6(&trap->sip)->addr); 337 #endif 338 } else { 339 #if LWIP_IPV4 340 len = sizeof(ip_2_ip4(&trap->sip)->addr); 341 #endif 342 } 343 snmp_asn1_enc_length_cnt(len, &lenlen); 344 tot_len += 1 + len + lenlen; 345 346 snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len); 347 snmp_asn1_enc_length_cnt(len, &lenlen); 348 tot_len += 1 + len + lenlen; 349 350 trap->pdulen = tot_len; 351 snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen); 352 tot_len += 1 + lenlen; 353 354 trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF); 355 snmp_asn1_enc_length_cnt(trap->comlen, &lenlen); 356 tot_len += 1 + lenlen + trap->comlen; 357 358 snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len); 359 snmp_asn1_enc_length_cnt(len, &lenlen); 360 tot_len += 1 + len + lenlen; 361 362 trap->seqlen = tot_len; 363 snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen); 364 tot_len += 1 + lenlen; 365 366 return tot_len; 367 } 368 369 static err_t 370 snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds) 371 { 372 struct snmp_asn1_tlv tlv; 373 struct snmp_varbind *varbind; 374 375 varbind = varbinds; 376 377 SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen); 378 BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); 379 380 while (varbind != NULL) { 381 BUILD_EXEC( snmp_append_outbound_varbind(pbuf_stream, varbind) ); 382 383 varbind = varbind->next; 384 } 385 386 return ERR_OK; 387 } 388 389 /** 390 * Encodes trap header from head to tail. 391 */ 392 static err_t 393 snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream) 394 { 395 struct snmp_asn1_tlv tlv; 396 397 /* 'Message' sequence */ 398 SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen); 399 BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); 400 401 /* version */ 402 SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); 403 snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len); 404 BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); 405 BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version) ); 406 407 /* community */ 408 SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen); 409 BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); 410 BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)snmp_community_trap, trap->comlen) ); 411 412 /* 'PDU' sequence */ 413 SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen); 414 BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); 415 416 /* object ID */ 417 SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0); 418 snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len); 419 BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); 420 BUILD_EXEC( snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len) ); 421 422 /* IP addr */ 423 if (IP_IS_V6_VAL(trap->sip)) { 424 #if LWIP_IPV6 425 SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr)); 426 BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); 427 BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr)) ); 428 #endif 429 } else { 430 #if LWIP_IPV4 431 SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr)); 432 BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); 433 BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr)) ); 434 #endif 435 } 436 437 /* trap length */ 438 SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); 439 snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len); 440 BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); 441 BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap) ); 442 443 /* specific trap */ 444 SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); 445 snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len); 446 BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); 447 BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap) ); 448 449 /* timestamp */ 450 SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0); 451 snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len); 452 BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); 453 BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts) ); 454 455 return ERR_OK; 456 } 457 458 #endif /* LWIP_SNMP */ 459