trap.c revision 216294
1/* 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Author: Harti Brandt <harti@freebsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $Begemot: bsnmp/snmpd/trap.c,v 1.9 2005/10/04 11:21:39 brandt_h Exp $ 30 * 31 * TrapSinkTable 32 */ 33#include <sys/types.h> 34#include <sys/queue.h> 35#include <sys/sysctl.h> 36#include <sys/un.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <stdarg.h> 40#include <stdarg.h> 41#include <string.h> 42#include <ctype.h> 43#include <syslog.h> 44#include <unistd.h> 45#include <netinet/in.h> 46#include <arpa/inet.h> 47 48#include "snmpmod.h" 49#include "snmpd.h" 50#include "tree.h" 51#include "oid.h" 52 53struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list); 54 55static const struct asn_oid oid_begemotTrapSinkTable = 56 OIDX_begemotTrapSinkTable; 57static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime; 58static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID; 59 60struct trapsink_dep { 61 struct snmp_dependency dep; 62 u_int set; 63 u_int status; 64 u_char comm[SNMP_COMMUNITY_MAXLEN + 1]; 65 u_int version; 66 u_int rb; 67 u_int rb_status; 68 u_int rb_version; 69 u_char rb_comm[SNMP_COMMUNITY_MAXLEN + 1]; 70}; 71enum { 72 TDEP_STATUS = 0x0001, 73 TDEP_COMM = 0x0002, 74 TDEP_VERSION = 0x0004, 75 76 TDEP_CREATE = 0x0001, 77 TDEP_MODIFY = 0x0002, 78 TDEP_DESTROY = 0x0004, 79}; 80 81static int 82trapsink_create(struct trapsink_dep *tdep) 83{ 84 struct trapsink *t; 85 struct sockaddr_in sa; 86 87 if ((t = malloc(sizeof(*t))) == NULL) 88 return (SNMP_ERR_RES_UNAVAIL); 89 90 t->index = tdep->dep.idx; 91 t->status = TRAPSINK_NOT_READY; 92 t->comm[0] = '\0'; 93 t->version = TRAPSINK_V2; 94 95 if ((t->socket = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { 96 syslog(LOG_ERR, "socket(UDP): %m"); 97 free(t); 98 return (SNMP_ERR_RES_UNAVAIL); 99 } 100 (void)shutdown(t->socket, SHUT_RD); 101 102 sa.sin_len = sizeof(sa); 103 sa.sin_family = AF_INET; 104 sa.sin_addr.s_addr = htonl((t->index.subs[0] << 24) | 105 (t->index.subs[1] << 16) | (t->index.subs[2] << 8) | 106 (t->index.subs[3] << 0)); 107 sa.sin_port = htons(t->index.subs[4]); 108 109 if (connect(t->socket, (struct sockaddr *)&sa, sa.sin_len) == -1) { 110 syslog(LOG_ERR, "connect(%s,%u): %m", 111 inet_ntoa(sa.sin_addr), ntohs(sa.sin_port)); 112 (void)close(t->socket); 113 free(t); 114 return (SNMP_ERR_GENERR); 115 } 116 117 if (tdep->set & TDEP_VERSION) 118 t->version = tdep->version; 119 if (tdep->set & TDEP_COMM) 120 strcpy(t->comm, tdep->comm); 121 122 if (t->comm[0] != '\0') 123 t->status = TRAPSINK_NOT_IN_SERVICE; 124 125 /* look whether we should activate */ 126 if (tdep->status == 4) { 127 if (t->status == TRAPSINK_NOT_READY) { 128 if (t->socket != -1) 129 (void)close(t->socket); 130 free(t); 131 return (SNMP_ERR_INCONS_VALUE); 132 } 133 t->status = TRAPSINK_ACTIVE; 134 } 135 136 INSERT_OBJECT_OID(t, &trapsink_list); 137 138 tdep->rb |= TDEP_CREATE; 139 140 return (SNMP_ERR_NOERROR); 141} 142 143static void 144trapsink_free(struct trapsink *t) 145{ 146 TAILQ_REMOVE(&trapsink_list, t, link); 147 if (t->socket != -1) 148 (void)close(t->socket); 149 free(t); 150} 151 152static int 153trapsink_modify(struct trapsink *t, struct trapsink_dep *tdep) 154{ 155 tdep->rb_status = t->status; 156 tdep->rb_version = t->version; 157 strcpy(tdep->rb_comm, t->comm); 158 159 if (tdep->set & TDEP_STATUS) { 160 /* if we are active and should move to not_in_service do 161 * this first */ 162 if (tdep->status == 2 && tdep->rb_status == TRAPSINK_ACTIVE) { 163 t->status = TRAPSINK_NOT_IN_SERVICE; 164 tdep->rb |= TDEP_MODIFY; 165 } 166 } 167 168 if (tdep->set & TDEP_VERSION) 169 t->version = tdep->version; 170 if (tdep->set & TDEP_COMM) 171 strcpy(t->comm, tdep->comm); 172 173 if (tdep->set & TDEP_STATUS) { 174 /* if we were inactive and should go active - do this now */ 175 if (tdep->status == 1 && tdep->rb_status != TRAPSINK_ACTIVE) { 176 if (t->comm[0] == '\0') { 177 t->status = tdep->rb_status; 178 t->version = tdep->rb_version; 179 strcpy(t->comm, tdep->rb_comm); 180 return (SNMP_ERR_INCONS_VALUE); 181 } 182 t->status = TRAPSINK_ACTIVE; 183 tdep->rb |= TDEP_MODIFY; 184 } 185 } 186 return (SNMP_ERR_NOERROR); 187} 188 189static int 190trapsink_unmodify(struct trapsink *t, struct trapsink_dep *tdep) 191{ 192 if (tdep->set & TDEP_STATUS) 193 t->status = tdep->rb_status; 194 if (tdep->set & TDEP_VERSION) 195 t->version = tdep->rb_version; 196 if (tdep->set & TDEP_COMM) 197 strcpy(t->comm, tdep->rb_comm); 198 199 return (SNMP_ERR_NOERROR); 200} 201 202static int 203trapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t, 204 struct trapsink_dep *tdep) 205{ 206 t->status = TRAPSINK_DESTROY; 207 tdep->rb_status = t->status; 208 tdep->rb |= TDEP_DESTROY; 209 return (SNMP_ERR_NOERROR); 210} 211 212static int 213trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep) 214{ 215 t->status = tdep->rb_status; 216 return (SNMP_ERR_NOERROR); 217} 218 219static int 220trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep, 221 enum snmp_depop op) 222{ 223 struct trapsink_dep *tdep = (struct trapsink_dep *)dep; 224 struct trapsink *t; 225 226 t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0); 227 228 switch (op) { 229 230 case SNMP_DEPOP_COMMIT: 231 if (tdep->set & TDEP_STATUS) { 232 switch (tdep->status) { 233 234 case 1: 235 case 2: 236 if (t == NULL) 237 return (SNMP_ERR_INCONS_VALUE); 238 return (trapsink_modify(t, tdep)); 239 240 case 4: 241 case 5: 242 if (t != NULL) 243 return (SNMP_ERR_INCONS_VALUE); 244 return (trapsink_create(tdep)); 245 246 case 6: 247 if (t == NULL) 248 return (SNMP_ERR_NOERROR); 249 return (trapsink_destroy(ctx, t, tdep)); 250 } 251 } else if (tdep->set != 0) 252 return (trapsink_modify(t, tdep)); 253 254 return (SNMP_ERR_NOERROR); 255 256 case SNMP_DEPOP_ROLLBACK: 257 if (tdep->rb & TDEP_CREATE) { 258 trapsink_free(t); 259 return (SNMP_ERR_NOERROR); 260 } 261 if (tdep->rb & TDEP_MODIFY) 262 return (trapsink_unmodify(t, tdep)); 263 if(tdep->rb & TDEP_DESTROY) 264 return (trapsink_undestroy(t, tdep)); 265 return (SNMP_ERR_NOERROR); 266 267 case SNMP_DEPOP_FINISH: 268 if ((tdep->rb & TDEP_DESTROY) && t != NULL && 269 ctx->code == SNMP_RET_OK) 270 trapsink_free(t); 271 return (SNMP_ERR_NOERROR); 272 } 273 abort(); 274} 275 276int 277op_trapsink(struct snmp_context *ctx, struct snmp_value *value, 278 u_int sub, u_int iidx, enum snmp_op op) 279{ 280 struct trapsink *t; 281 u_char ipa[4]; 282 int32_t port; 283 struct asn_oid idx; 284 struct trapsink_dep *tdep; 285 u_char *p; 286 287 t = NULL; /* gcc */ 288 289 switch (op) { 290 291 case SNMP_OP_GETNEXT: 292 if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) 293 return (SNMP_ERR_NOSUCHNAME); 294 index_append(&value->var, sub, &t->index); 295 break; 296 297 case SNMP_OP_GET: 298 if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) 299 return (SNMP_ERR_NOSUCHNAME); 300 break; 301 302 case SNMP_OP_SET: 303 if (index_decode(&value->var, sub, iidx, ipa, &port) || 304 port == 0 || port > 65535) 305 return (SNMP_ERR_NO_CREATION); 306 t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub); 307 308 asn_slice_oid(&idx, &value->var, sub, value->var.len); 309 310 tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx, 311 &oid_begemotTrapSinkTable, &idx, 312 sizeof(*tdep), trapsink_dep); 313 if (tdep == NULL) 314 return (SNMP_ERR_RES_UNAVAIL); 315 316 switch (value->var.subs[sub - 1]) { 317 318 case LEAF_begemotTrapSinkStatus: 319 if (tdep->set & TDEP_STATUS) 320 return (SNMP_ERR_INCONS_VALUE); 321 switch (value->v.integer) { 322 323 case 1: 324 case 2: 325 if (t == NULL) 326 return (SNMP_ERR_INCONS_VALUE); 327 break; 328 329 case 4: 330 case 5: 331 if (t != NULL) 332 return (SNMP_ERR_INCONS_VALUE); 333 break; 334 335 case 6: 336 break; 337 338 default: 339 return (SNMP_ERR_WRONG_VALUE); 340 } 341 tdep->status = value->v.integer; 342 tdep->set |= TDEP_STATUS; 343 return (SNMP_ERR_NOERROR); 344 345 case LEAF_begemotTrapSinkComm: 346 if (tdep->set & TDEP_COMM) 347 return (SNMP_ERR_INCONS_VALUE); 348 if (value->v.octetstring.len == 0 || 349 value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN) 350 return (SNMP_ERR_WRONG_VALUE); 351 for (p = value->v.octetstring.octets; 352 p < value->v.octetstring.octets + value->v.octetstring.len; 353 p++) { 354 if (!isascii(*p) || !isprint(*p)) 355 return (SNMP_ERR_WRONG_VALUE); 356 } 357 tdep->set |= TDEP_COMM; 358 strncpy(tdep->comm, value->v.octetstring.octets, 359 value->v.octetstring.len); 360 tdep->comm[value->v.octetstring.len] = '\0'; 361 return (SNMP_ERR_NOERROR); 362 363 case LEAF_begemotTrapSinkVersion: 364 if (tdep->set & TDEP_VERSION) 365 return (SNMP_ERR_INCONS_VALUE); 366 if (value->v.integer != TRAPSINK_V1 && 367 value->v.integer != TRAPSINK_V2) 368 return (SNMP_ERR_WRONG_VALUE); 369 tdep->version = value->v.integer; 370 tdep->set |= TDEP_VERSION; 371 return (SNMP_ERR_NOERROR); 372 } 373 if (t == NULL) 374 return (SNMP_ERR_INCONS_NAME); 375 else 376 return (SNMP_ERR_NOT_WRITEABLE); 377 378 379 case SNMP_OP_ROLLBACK: 380 case SNMP_OP_COMMIT: 381 return (SNMP_ERR_NOERROR); 382 } 383 384 switch (value->var.subs[sub - 1]) { 385 386 case LEAF_begemotTrapSinkStatus: 387 value->v.integer = t->status; 388 break; 389 390 case LEAF_begemotTrapSinkComm: 391 return (string_get(value, t->comm, -1)); 392 393 case LEAF_begemotTrapSinkVersion: 394 value->v.integer = t->version; 395 break; 396 397 } 398 return (SNMP_ERR_NOERROR); 399} 400 401void 402snmp_send_trap(const struct asn_oid *trap_oid, ...) 403{ 404 struct snmp_pdu pdu; 405 struct trapsink *t; 406 const struct snmp_value *v; 407 va_list ap; 408 u_char *sndbuf; 409 size_t sndlen; 410 ssize_t len; 411 412 TAILQ_FOREACH(t, &trapsink_list, link) { 413 if (t->status != TRAPSINK_ACTIVE) 414 continue; 415 memset(&pdu, 0, sizeof(pdu)); 416 strcpy(pdu.community, t->comm); 417 if (t->version == TRAPSINK_V1) { 418 pdu.version = SNMP_V1; 419 pdu.type = SNMP_PDU_TRAP; 420 pdu.enterprise = systemg.object_id; 421 memcpy(pdu.agent_addr, snmpd.trap1addr, 4); 422 pdu.generic_trap = trap_oid->subs[trap_oid->len - 1] - 1; 423 pdu.specific_trap = 0; 424 pdu.time_stamp = get_ticks() - start_tick; 425 426 pdu.nbindings = 0; 427 } else { 428 pdu.version = SNMP_V2c; 429 pdu.type = SNMP_PDU_TRAP2; 430 pdu.request_id = reqid_next(trap_reqid); 431 pdu.error_index = 0; 432 pdu.error_status = SNMP_ERR_NOERROR; 433 434 pdu.bindings[0].var = oid_sysUpTime; 435 pdu.bindings[0].var.subs[pdu.bindings[0].var.len++] = 0; 436 pdu.bindings[0].syntax = SNMP_SYNTAX_TIMETICKS; 437 pdu.bindings[0].v.uint32 = get_ticks() - start_tick; 438 439 pdu.bindings[1].var = oid_snmpTrapOID; 440 pdu.bindings[1].var.subs[pdu.bindings[1].var.len++] = 0; 441 pdu.bindings[1].syntax = SNMP_SYNTAX_OID; 442 pdu.bindings[1].v.oid = *trap_oid; 443 444 pdu.nbindings = 2; 445 } 446 447 va_start(ap, trap_oid); 448 while ((v = va_arg(ap, const struct snmp_value *)) != NULL) 449 pdu.bindings[pdu.nbindings++] = *v; 450 va_end(ap); 451 452 if ((sndbuf = buf_alloc(1)) == NULL) { 453 syslog(LOG_ERR, "trap send buffer: %m"); 454 return; 455 } 456 457 snmp_output(&pdu, sndbuf, &sndlen, "TRAP"); 458 459 if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1) 460 syslog(LOG_ERR, "send: %m"); 461 else if ((size_t)len != sndlen) 462 syslog(LOG_ERR, "send: short write %zu/%zu", 463 sndlen, (size_t)len); 464 465 free(sndbuf); 466 } 467} 468