1 /* $OpenBSD: trap.c,v 1.22 2014/04/14 12:55:10 blambert Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/param.h> 21 #include <sys/types.h> 22 #include <sys/stat.h> 23 #include <sys/socket.h> 24 #include <sys/un.h> 25 #include <sys/tree.h> 26 27 #include <net/if.h> 28 #include <netinet/in.h> 29 #include <arpa/inet.h> 30 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include <errno.h> 34 #include <event.h> 35 #include <fcntl.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <pwd.h> 39 40 #include "snmpd.h" 41 #include "mib.h" 42 43 extern struct snmpd *env; 44 45 void 46 trap_init(void) 47 { 48 struct ber_oid trapoid = OID(MIB_coldStart); 49 50 /* 51 * Send a coldStart to notify that the daemon has been 52 * started and re-initialized. 53 */ 54 trap_send(&trapoid, NULL); 55 } 56 57 int 58 trap_agentx(struct agentx_handle *h, struct agentx_pdu *pdu, int *idx, 59 char **varcpy, int *vcpylen) 60 { 61 struct agentx_varbind_hdr vbhdr; 62 struct ber_oid o, oid; 63 struct ber_oid uptime = OID(MIB_sysUpTime); 64 struct ber_oid trapoid = OID(MIB_snmpTrapOID); 65 u_int32_t d; 66 u_int64_t l; 67 char *str; 68 int slen; 69 struct ber_element *a, *ber, *varbind; 70 int x = 0, state = 0; 71 int ret = AGENTX_ERR_NONE; 72 int seensysuptime, seentrapoid; 73 size_t len = 0; 74 pid_t pid = -1; 75 char *v = NULL; 76 77 *varcpy = NULL; 78 ber = varbind = NULL; 79 seensysuptime = seentrapoid = 0; 80 81 if (pdu->hdr->flags & AGENTX_NON_DEFAULT_CONTEXT) { 82 ret = AGENTX_ERR_UNSUPPORTED_CONTEXT; 83 goto done; 84 } 85 86 if ((v = malloc(pdu->hdr->length)) == NULL || 87 snmp_agentx_copy_raw(pdu, v, pdu->hdr->length) == -1) { 88 ret = AGENTX_ERR_PROCESSING_ERROR; 89 goto done; 90 } 91 92 while (pdu->datalen > sizeof(struct agentx_hdr)) { 93 x++; 94 95 if (snmp_agentx_read_vbhdr(pdu, &vbhdr) == -1 || 96 snmp_agentx_read_oid(pdu, (struct snmp_oid *)&oid) == -1) { 97 ret = AGENTX_ERR_PARSE_ERROR; 98 goto done; 99 } 100 if (state < 2) { 101 if (state == 0 && ber_oid_cmp(&oid, &uptime) == 0) { 102 if (snmp_agentx_read_int(pdu, &d) == -1) { 103 ret = AGENTX_ERR_PARSE_ERROR; 104 goto done; 105 } 106 state = 1; 107 continue; 108 } else if (ber_oid_cmp(&oid, &trapoid) == 0) { 109 if (snmp_agentx_read_oid(pdu, 110 (struct snmp_oid *)&o) == -1) { 111 ret = AGENTX_ERR_PARSE_ERROR; 112 goto done; 113 } 114 state = 2; 115 continue; 116 } else { 117 ret = AGENTX_ERR_PROCESSING_ERROR; 118 goto done; 119 } 120 } 121 122 ber = ber_add_sequence(ber); 123 if (varbind == NULL) 124 varbind = ber; 125 126 a = ber_add_oid(ber, &oid); 127 128 switch (vbhdr.type) { 129 case AGENTX_NO_SUCH_OBJECT: 130 case AGENTX_NO_SUCH_INSTANCE: 131 case AGENTX_END_OF_MIB_VIEW: 132 case AGENTX_NULL: 133 a = ber_add_null(a); 134 break; 135 136 case AGENTX_IP_ADDRESS: 137 case AGENTX_OPAQUE: 138 case AGENTX_OCTET_STRING: 139 str = snmp_agentx_read_octetstr(pdu, &slen); 140 if (str == NULL) { 141 ret = AGENTX_ERR_PARSE_ERROR; 142 goto done; 143 } 144 a = ber_add_nstring(a, str, slen); 145 break; 146 147 case AGENTX_OBJECT_IDENTIFIER: 148 if (snmp_agentx_read_oid(pdu, 149 (struct snmp_oid *)&oid) == -1) { 150 ret = AGENTX_ERR_PARSE_ERROR; 151 goto done; 152 } 153 a = ber_add_oid(a, &oid); 154 break; 155 156 case AGENTX_INTEGER: 157 case AGENTX_COUNTER32: 158 case AGENTX_GAUGE32: 159 case AGENTX_TIME_TICKS: 160 if (snmp_agentx_read_int(pdu, &d) == -1) { 161 ret = AGENTX_ERR_PARSE_ERROR; 162 goto done; 163 } 164 a = ber_add_integer(a, d); 165 break; 166 167 case AGENTX_COUNTER64: 168 if (snmp_agentx_read_int64(pdu, &l) == -1) { 169 ret = AGENTX_ERR_PARSE_ERROR; 170 goto done; 171 } 172 a = ber_add_integer(a, l); 173 break; 174 175 default: 176 log_debug("unknown data type '%i'", vbhdr.type); 177 ret = AGENTX_ERR_PARSE_ERROR; 178 goto done; 179 } 180 181 /* AgentX types correspond to BER types */ 182 switch (vbhdr.type) { 183 case BER_TYPE_INTEGER: 184 case BER_TYPE_BITSTRING: 185 case BER_TYPE_OCTETSTRING: 186 case BER_TYPE_NULL: 187 case BER_TYPE_OBJECT: 188 /* universal types */ 189 break; 190 default: 191 /* application-specific types */ 192 ber_set_header(a, BER_CLASS_APPLICATION, vbhdr.type); 193 break; 194 } 195 } 196 197 if (varbind != NULL) 198 len = ber_calc_len(varbind); 199 log_debug("trap_agentx: from pid %u len %d elements %d", 200 pid, len, x); 201 202 trap_send(&o, varbind); 203 204 *varcpy = v; 205 *vcpylen = pdu->hdr->length; 206 207 return (AGENTX_ERR_NONE); 208 done: 209 if (varbind != NULL) 210 ber_free_elements(varbind); 211 if (v) 212 free(v); 213 *idx = x; 214 return (ret); 215 } 216 217 int 218 trap_send(struct ber_oid *oid, struct ber_element *elm) 219 { 220 int ret = 0, s; 221 struct address *tr; 222 struct ber_element *root, *b, *c, *trap; 223 struct ber ber; 224 char *cmn; 225 ssize_t len; 226 u_int8_t *ptr; 227 struct ber_oid uptime = OID(MIB_sysUpTime); 228 struct ber_oid trapoid = OID(MIB_snmpTrapOID); 229 char ostr[SNMP_MAX_OID_STRLEN]; 230 struct oid oa, ob; 231 232 if (TAILQ_EMPTY(&env->sc_trapreceivers)) 233 return (0); 234 235 smi_scalar_oidlen(&uptime); 236 smi_scalar_oidlen(&trapoid); 237 smi_scalar_oidlen(oid); 238 239 smi_oid2string(oid, ostr, sizeof(ostr), 0); 240 log_debug("trap_send: oid %s", ostr); 241 242 /* Setup OIDs to compare against the trap receiver MIB */ 243 bzero(&oa, sizeof(oa)); 244 bcopy(oid->bo_id, &oa.o_oid, sizeof(oa.o_oid)); 245 oa.o_oidlen = oid->bo_n; 246 bzero(&ob, sizeof(ob)); 247 ob.o_flags = OID_TABLE; 248 249 /* Add mandatory varbind elements */ 250 trap = ber_add_sequence(NULL); 251 c = ber_printf_elements(trap, "{Odt}{OO}", 252 &uptime, smi_getticks(), 253 BER_CLASS_APPLICATION, SNMP_T_TIMETICKS, 254 &trapoid, oid); 255 if (elm != NULL) 256 ber_link_elements(c, elm); 257 258 bzero(&ber, sizeof(ber)); 259 ber.fd = -1; 260 261 TAILQ_FOREACH(tr, &env->sc_trapreceivers, entry) { 262 if (tr->sa_oid != NULL && tr->sa_oid->bo_n) { 263 /* The trap receiver may want only a specified MIB */ 264 bcopy(&tr->sa_oid->bo_id, &ob.o_oid, 265 sizeof(ob.o_oid)); 266 ob.o_oidlen = tr->sa_oid->bo_n; 267 if (smi_oid_cmp(&oa, &ob) != 0) 268 continue; 269 } 270 271 if ((s = snmpd_socket_af(&tr->ss, htons(tr->port))) == -1) { 272 ret = -1; 273 goto done; 274 } 275 276 cmn = tr->sa_community != NULL ? 277 tr->sa_community : env->sc_trcommunity; 278 279 /* SNMP header */ 280 root = ber_add_sequence(NULL); 281 b = ber_printf_elements(root, "ds{tddd", 282 SNMP_V2, cmn, BER_CLASS_CONTEXT, SNMP_C_TRAPV2, 283 arc4random(), 0, 0); 284 ber_link_elements(b, trap); 285 286 #ifdef DEBUG 287 smi_debug_elements(root); 288 #endif 289 len = ber_write_elements(&ber, root); 290 if (ber_get_writebuf(&ber, (void *)&ptr) > 0 && 291 sendto(s, ptr, len, 0, (struct sockaddr *)&tr->ss, 292 tr->ss.ss_len) != -1) { 293 env->sc_stats.snmp_outpkts++; 294 ret++; 295 } 296 297 close(s); 298 ber_unlink_elements(b); 299 ber_free_elements(root); 300 } 301 302 done: 303 ber_free_elements(trap); 304 ber_free(&ber); 305 306 return (ret); 307 } 308