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.7 2004/04/13 14:58:46 novo 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 56 struct trapsink_list trapsink_list = TAILQ_HEAD_INITIALIZER(trapsink_list); 57 58 static const struct asn_oid oid_begemotTrapSinkTable = 59 OIDX_begemotTrapSinkTable; 60 static const struct asn_oid oid_sysUpTime = OIDX_sysUpTime; 61 static const struct asn_oid oid_snmpTrapOID = OIDX_snmpTrapOID; 62 63 struct 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 }; 74 enum { 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 84 static int 85 trapsink_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 146 static void 147 trapsink_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 155 static int 156 trapsink_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 192 static int 193 trapsink_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 205 static int 206 trapsink_destroy(struct snmp_context *ctx __unused, struct trapsink *t, 207 struct trapsink_dep *tdep) 208 { 209 t->status = TRAPSINK_DESTROY; 210 tdep->rb_status = t->status; 211 tdep->rb |= TDEP_DESTROY; 212 return (SNMP_ERR_NOERROR); 213 } 214 215 static int 216 trapsink_undestroy(struct trapsink *t, struct trapsink_dep *tdep) 217 { 218 t->status = tdep->rb_status; 219 return (SNMP_ERR_NOERROR); 220 } 221 222 static int 223 trapsink_dep(struct snmp_context *ctx, struct snmp_dependency *dep, 224 enum snmp_depop op) 225 { 226 struct trapsink_dep *tdep = (struct trapsink_dep *)dep; 227 struct trapsink *t; 228 229 t = FIND_OBJECT_OID(&trapsink_list, &dep->idx, 0); 230 231 switch (op) { 232 233 case SNMP_DEPOP_COMMIT: 234 if (tdep->set & TDEP_STATUS) { 235 switch (tdep->status) { 236 237 case 1: 238 case 2: 239 if (t == NULL) 240 return (SNMP_ERR_INCONS_VALUE); 241 return (trapsink_modify(t, tdep)); 242 243 case 4: 244 case 5: 245 if (t != NULL) 246 return (SNMP_ERR_INCONS_VALUE); 247 return (trapsink_create(tdep)); 248 249 case 6: 250 if (t == NULL) 251 return (SNMP_ERR_NOERROR); 252 return (trapsink_destroy(ctx, t, tdep)); 253 } 254 } else if (tdep->set != 0) 255 return (trapsink_modify(t, tdep)); 256 257 return (SNMP_ERR_NOERROR); 258 259 case SNMP_DEPOP_ROLLBACK: 260 if (tdep->rb & TDEP_CREATE) { 261 trapsink_free(t); 262 return (SNMP_ERR_NOERROR); 263 } 264 if (tdep->rb & TDEP_MODIFY) 265 return (trapsink_unmodify(t, tdep)); 266 if(tdep->rb & TDEP_DESTROY) 267 return (trapsink_undestroy(t, tdep)); 268 return (SNMP_ERR_NOERROR); 269 270 case SNMP_DEPOP_FINISH: 271 if ((tdep->rb & TDEP_DESTROY) && t != NULL && 272 ctx->code == SNMP_RET_OK) 273 trapsink_free(t); 274 return (SNMP_ERR_NOERROR); 275 } 276 abort(); 277 } 278 279 int 280 op_trapsink(struct snmp_context *ctx, struct snmp_value *value, 281 u_int sub, u_int iidx, enum snmp_op op) 282 { 283 struct trapsink *t; 284 u_char ipa[4]; 285 int32_t port; 286 struct asn_oid idx; 287 struct trapsink_dep *tdep; 288 u_char *p; 289 290 t = NULL; /* gcc */ 291 292 switch (op) { 293 294 case SNMP_OP_GETNEXT: 295 if ((t = NEXT_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) 296 return (SNMP_ERR_NOSUCHNAME); 297 index_append(&value->var, sub, &t->index); 298 break; 299 300 case SNMP_OP_GET: 301 if ((t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub)) == NULL) 302 return (SNMP_ERR_NOSUCHNAME); 303 break; 304 305 case SNMP_OP_SET: 306 if (index_decode(&value->var, sub, iidx, ipa, &port) || 307 port == 0 || port > 65535) 308 return (SNMP_ERR_NO_CREATION); 309 t = FIND_OBJECT_OID(&trapsink_list, &value->var, sub); 310 311 asn_slice_oid(&idx, &value->var, sub, value->var.len); 312 313 tdep = (struct trapsink_dep *)snmp_dep_lookup(ctx, 314 &oid_begemotTrapSinkTable, &idx, 315 sizeof(*tdep), trapsink_dep); 316 if (tdep == NULL) 317 return (SNMP_ERR_RES_UNAVAIL); 318 319 switch (value->var.subs[sub - 1]) { 320 321 case LEAF_begemotTrapSinkStatus: 322 if (tdep->set & TDEP_STATUS) 323 return (SNMP_ERR_INCONS_VALUE); 324 switch (value->v.integer) { 325 326 case 1: 327 case 2: 328 if (t == NULL) 329 return (SNMP_ERR_INCONS_VALUE); 330 break; 331 332 case 4: 333 case 5: 334 if (t != NULL) 335 return (SNMP_ERR_INCONS_VALUE); 336 break; 337 338 case 6: 339 break; 340 341 default: 342 return (SNMP_ERR_WRONG_VALUE); 343 } 344 tdep->status = value->v.integer; 345 tdep->set |= TDEP_STATUS; 346 return (SNMP_ERR_NOERROR); 347 348 case LEAF_begemotTrapSinkComm: 349 if (tdep->set & TDEP_COMM) 350 return (SNMP_ERR_INCONS_VALUE); 351 if (value->v.octetstring.len == 0 || 352 value->v.octetstring.len > SNMP_COMMUNITY_MAXLEN) 353 return (SNMP_ERR_WRONG_VALUE); 354 for (p = value->v.octetstring.octets; 355 p < value->v.octetstring.octets + value->v.octetstring.len; 356 p++) { 357 if (!isascii(*p) || !isprint(*p)) 358 return (SNMP_ERR_WRONG_VALUE); 359 } 360 tdep->set |= TDEP_COMM; 361 strncpy(tdep->comm, value->v.octetstring.octets, 362 value->v.octetstring.len); 363 tdep->comm[value->v.octetstring.len] = '\0'; 364 return (SNMP_ERR_NOERROR); 365 366 case LEAF_begemotTrapSinkVersion: 367 if (tdep->set & TDEP_VERSION) 368 return (SNMP_ERR_INCONS_VALUE); 369 if (value->v.integer != TRAPSINK_V1 && 370 value->v.integer != TRAPSINK_V2) 371 return (SNMP_ERR_WRONG_VALUE); 372 tdep->version = value->v.integer; 373 tdep->set |= TDEP_VERSION; 374 return (SNMP_ERR_NOERROR); 375 } 376 if (t == NULL) 377 return (SNMP_ERR_INCONS_NAME); 378 else 379 return (SNMP_ERR_NOT_WRITEABLE); 380 381 382 case SNMP_OP_ROLLBACK: 383 case SNMP_OP_COMMIT: 384 return (SNMP_ERR_NOERROR); 385 } 386 387 switch (value->var.subs[sub - 1]) { 388 389 case LEAF_begemotTrapSinkStatus: 390 value->v.integer = t->status; 391 break; 392 393 case LEAF_begemotTrapSinkComm: 394 return (string_get(value, t->comm, -1)); 395 396 case LEAF_begemotTrapSinkVersion: 397 value->v.integer = t->version; 398 break; 399 400 } 401 return (SNMP_ERR_NOERROR); 402 } 403 404 void 405 snmp_send_trap(const struct asn_oid *trap_oid, ...) 406 { 407 struct snmp_pdu pdu; 408 struct trapsink *t; 409 const struct snmp_value *v; 410 va_list ap; 411 u_char *sndbuf; 412 size_t sndlen; 413 ssize_t len; 414 415 TAILQ_FOREACH(t, &trapsink_list, link) { 416 if (t->status != TRAPSINK_ACTIVE) 417 continue; 418 memset(&pdu, 0, sizeof(pdu)); 419 strcpy(pdu.community, t->comm); 420 if (t->version == TRAPSINK_V1) { 421 pdu.version = SNMP_V1; 422 pdu.type = SNMP_PDU_TRAP; 423 pdu.enterprise = systemg.object_id; 424 memcpy(pdu.agent_addr, snmpd.trap1addr, 4); 425 pdu.generic_trap = trap_oid->subs[trap_oid->len - 1] - 1; 426 pdu.specific_trap = 0; 427 pdu.time_stamp = get_ticks() - start_tick; 428 429 pdu.nbindings = 0; 430 } else { 431 pdu.version = SNMP_V2c; 432 pdu.type = SNMP_PDU_TRAP2; 433 pdu.request_id = reqid_next(trap_reqid); 434 pdu.error_index = 0; 435 pdu.error_status = SNMP_ERR_NOERROR; 436 437 pdu.bindings[0].var = oid_sysUpTime; 438 pdu.bindings[0].var.subs[pdu.bindings[0].var.len++] = 0; 439 pdu.bindings[0].syntax = SNMP_SYNTAX_TIMETICKS; 440 pdu.bindings[0].v.uint32 = get_ticks() - start_tick; 441 442 pdu.bindings[1].var = oid_snmpTrapOID; 443 pdu.bindings[1].var.subs[pdu.bindings[1].var.len++] = 0; 444 pdu.bindings[1].syntax = SNMP_SYNTAX_OID; 445 pdu.bindings[1].v.oid = *trap_oid; 446 447 pdu.nbindings = 2; 448 } 449 450 va_start(ap, trap_oid); 451 while ((v = va_arg(ap, const struct snmp_value *)) != NULL) 452 pdu.bindings[pdu.nbindings++] = *v; 453 va_end(ap); 454 455 if ((sndbuf = buf_alloc(1)) == NULL) { 456 syslog(LOG_ERR, "trap send buffer: %m"); 457 return; 458 } 459 460 snmp_output(&pdu, sndbuf, &sndlen, "TRAP"); 461 462 if ((len = send(t->socket, sndbuf, sndlen, 0)) == -1) 463 syslog(LOG_ERR, "send: %m"); 464 else if ((size_t)len != sndlen) 465 syslog(LOG_ERR, "send: short write %zu/%zu", 466 sndlen, (size_t)len); 467 468 free(sndbuf); 469 } 470 } 471