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