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