]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/bsnmp/snmpd/trap.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / bsnmp / snmpd / trap.c
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 }