xref: /openbsd/usr.sbin/snmpd/trap.c (revision 91f110e0)
1 /*	$OpenBSD: trap.c,v 1.21 2013/10/19 14:18:39 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_imsg(struct imsgev *iev, pid_t pid)
59 {
60 	struct imsgbuf		*ibuf;
61 	struct imsg		 imsg;
62 	int			 ret = -1, n, x = 0, state = 0;
63 	int			 done = 0;
64 	struct snmp_imsg	*sm;
65 	u_int32_t		 d;
66 	u_int64_t		 l;
67 	u_int8_t		*c;
68 	char			 ostr[SNMP_MAX_OID_LEN];
69 	struct ber_element	*ber = NULL, *varbind = NULL, *a;
70 	size_t			 len = 0;
71 	struct			 ber_oid o;
72 
73 	ibuf = &iev->ibuf;
74 	while (!done) {
75 		while (!done) {
76 			if ((n = imsg_get(ibuf, &imsg)) == -1)
77 				goto done;
78 			if (n == 0)
79 				break;
80 
81 			switch (imsg.hdr.type) {
82 			case IMSG_SNMP_ELEMENT:
83 				if (imsg.hdr.len < (IMSG_HEADER_SIZE +
84 				    sizeof(struct snmp_imsg)))
85 					goto imsgdone;
86 
87 				sm = (struct snmp_imsg *)imsg.data;
88 
89 				if (!state++) {
90 					/* First element must be the trap OID */
91 					if (sm->snmp_type != SNMP_NULL)
92 						goto imsgdone;
93 					ber_string2oid(sm->snmp_oid, &o);
94 					break;
95 				}
96 
97 				ber = ber_add_sequence(ber);
98 				if (varbind == NULL)
99 					varbind = ber;
100 				a = ber_add_oidstring(ber, sm->snmp_oid);
101 
102 				switch (sm->snmp_type) {
103 				case SNMP_OBJECT:
104 					if (sm->snmp_len > sizeof(ostr) - 1)
105 						goto imsgdone;
106 					bzero(&ostr, sizeof(ostr));
107 					bcopy(sm + 1, &ostr, sm->snmp_len);
108 					a = ber_add_oidstring(a, ostr);
109 					break;
110 				case SNMP_BITSTRING:
111 					if (sm->snmp_len < 1)
112 						goto imsgdone;
113 					/* FALLTHROUGH */
114 				case SNMP_OCTETSTRING:
115 				case SNMP_IPADDR:
116 					if (sm->snmp_len >= SNMPD_MAXSTRLEN)
117 						goto imsgdone;
118 					c = (u_int8_t *)(sm + 1);
119 					if (sm->snmp_type == SNMP_BITSTRING)
120 						a = ber_add_bitstring(a, c,
121 						    sm->snmp_len);
122 					else
123 						a = ber_add_nstring(a, c,
124 						    sm->snmp_len);
125 					break;
126 				case SNMP_NULL:
127 					a = ber_add_null(a);
128 					break;
129 				case SNMP_INTEGER32:
130 				case SNMP_COUNTER32:
131 				case SNMP_GAUGE32:
132 				case SNMP_TIMETICKS:
133 				case SNMP_OPAQUE:
134 				case SNMP_UINTEGER32:
135 					if (sm->snmp_len != sizeof(d))
136 						goto imsgdone;
137 					bcopy(sm + 1, &d, sm->snmp_len);
138 					a = ber_add_integer(a, d);
139 					break;
140 				case SNMP_COUNTER64:
141 					if (sm->snmp_len != sizeof(l))
142 						goto imsgdone;
143 					bcopy(sm + 1, &l, sm->snmp_len);
144 					a = ber_add_integer(a, l);
145 					break;
146 				default:
147 					log_debug("trap_imsg: illegal type %d",
148 					    sm->snmp_type);
149 					imsg_free(&imsg);
150 					goto imsgdone;
151 				}
152 				switch (sm->snmp_type) {
153 				case SNMP_INTEGER32:
154 				case SNMP_BITSTRING:
155 				case SNMP_OCTETSTRING:
156 				case SNMP_NULL:
157 				case SNMP_OBJECT:
158 					/* universal types */
159 					break;
160 				default:
161 					/* application-specific types */
162 					ber_set_header(a, BER_CLASS_APPLICATION,
163 					    sm->snmp_type);
164 					break;
165 				}
166 				x++;
167 				break;
168 			case IMSG_SNMP_END:
169 				done = 1;
170 				break;
171 			default:
172 				log_debug("trap_imsg: illegal imsg %d",
173 				    imsg.hdr.type);
174 				goto imsgdone;
175 			}
176 			imsg_free(&imsg);
177 		}
178 		if (done)
179 			break;
180 		if ((n = imsg_read(ibuf)) == -1)
181 			goto done;
182 		if (n == 0)
183 			goto done;
184 	}
185 
186 	if (varbind != NULL)
187 		len = ber_calc_len(varbind);
188 	log_debug("trap_imsg: from pid %u len %d elements %d",
189 	    pid, len, x);
190 	trap_send(&o, varbind);
191 	return (0);
192 
193  imsgdone:
194 	imsg_free(&imsg);
195  done:
196 	if (varbind != NULL)
197 		ber_free_elements(varbind);
198 	return (ret);
199 }
200 
201 int
202 trap_send(struct ber_oid *oid, struct ber_element *elm)
203 {
204 	int			 ret = 0, s;
205 	struct address		*tr;
206 	struct ber_element	*root, *b, *c, *trap;
207 	struct ber		 ber;
208 	char			*cmn;
209 	ssize_t			 len;
210 	u_int8_t		*ptr;
211 	struct			 ber_oid uptime = OID(MIB_sysUpTime);
212 	struct			 ber_oid trapoid = OID(MIB_snmpTrapOID);
213 	char			 ostr[SNMP_MAX_OID_LEN];
214 	struct oid		 oa, ob;
215 
216 	if (TAILQ_EMPTY(&env->sc_trapreceivers))
217 		return (0);
218 
219 	smi_scalar_oidlen(&uptime);
220 	smi_scalar_oidlen(&trapoid);
221 	smi_scalar_oidlen(oid);
222 
223 	smi_oid2string(oid, ostr, sizeof(ostr), 0);
224 	log_debug("trap_send: oid %s", ostr);
225 
226 	/* Setup OIDs to compare against the trap receiver MIB */
227 	bzero(&oa, sizeof(oa));
228 	bcopy(oid->bo_id, &oa.o_oid, sizeof(oa.o_oid));
229 	oa.o_oidlen = oid->bo_n;
230 	bzero(&ob, sizeof(ob));
231 	ob.o_flags = OID_TABLE;
232 
233 	/* Add mandatory varbind elements */
234 	trap = ber_add_sequence(NULL);
235 	c = ber_printf_elements(trap, "{Odt}{OO}",
236 	    &uptime, smi_getticks(),
237 	    BER_CLASS_APPLICATION, SNMP_T_TIMETICKS,
238 	    &trapoid, oid);
239 	if (elm != NULL)
240 		ber_link_elements(c, elm);
241 
242 	bzero(&ber, sizeof(ber));
243 	ber.fd = -1;
244 
245 	TAILQ_FOREACH(tr, &env->sc_trapreceivers, entry) {
246 		if (tr->sa_oid != NULL && tr->sa_oid->bo_n) {
247 			/* The trap receiver may want only a specified MIB */
248 			bcopy(&tr->sa_oid->bo_id, &ob.o_oid,
249 			    sizeof(ob.o_oid));
250 			ob.o_oidlen = tr->sa_oid->bo_n;
251 			if (smi_oid_cmp(&oa, &ob) != 0)
252 				continue;
253 		}
254 
255 		if ((s = snmpd_socket_af(&tr->ss, htons(tr->port))) == -1) {
256 			ret = -1;
257 			goto done;
258 		}
259 
260 		cmn = tr->sa_community != NULL ?
261 		    tr->sa_community : env->sc_trcommunity;
262 
263 		/* SNMP header */
264 		root = ber_add_sequence(NULL);
265 		b = ber_printf_elements(root, "ds{tddd",
266 		    SNMP_V2, cmn, BER_CLASS_CONTEXT, SNMP_C_TRAPV2,
267 		    arc4random(), 0, 0);
268 		ber_link_elements(b, trap);
269 
270 #ifdef DEBUG
271 		smi_debug_elements(root);
272 #endif
273 
274 		len = ber_write_elements(&ber, root);
275 		if (ber_get_writebuf(&ber, (void *)&ptr) > 0 &&
276 		    sendto(s, ptr, len, 0, (struct sockaddr *)&tr->ss,
277 		    tr->ss.ss_len) != -1) {
278 			env->sc_stats.snmp_outpkts++;
279 			ret++;
280 		}
281 
282 		close(s);
283 		ber_unlink_elements(b);
284 		ber_free_elements(root);
285 	}
286 
287  done:
288 	ber_free_elements(trap);
289 	ber_free(&ber);
290 
291 	return (ret);
292 }
293