1 /*	$OpenBSD: application_agentx.c,v 1.16 2024/02/06 12:44:27 martijn Exp $ */
2 /*
3  * Copyright (c) 2022 Martijn van Duren <martijn@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/queue.h>
19 #include <sys/socket.h>
20 #include <sys/stat.h>
21 #include <sys/tree.h>
22 #include <sys/un.h>
23 
24 #include <ber.h>
25 #include <errno.h>
26 #include <event.h>
27 #include <inttypes.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 #include "application.h"
35 #include "ax.h"
36 #include "log.h"
37 #include "mib.h"
38 #include "smi.h"
39 #include "snmp.h"
40 #include "snmpd.h"
41 
42 #define AGENTX_DEFAULTTIMEOUT 5
43 
44 struct appl_agentx_connection {
45 	uint32_t conn_id;
46 	/*
47 	 * A backend has several overruling properties:
48 	 * - If it exits, snmpd crashes
49 	 * - All registrations are priority 1
50 	 * - All registrations own the subtree.
51 	 */
52 	int conn_backend;
53 	struct ax *conn_ax;
54 	struct event conn_rev;
55 	struct event conn_wev;
56 
57 	TAILQ_HEAD(, appl_agentx_session) conn_sessions;
58 	RB_ENTRY(appl_agentx_connection) conn_entry;
59 };
60 
61 struct appl_agentx_session {
62 	uint32_t sess_id;
63 	struct appl_agentx_connection *sess_conn;
64 	/*
65 	 * RFC 2741 section 7.1.1:
66 	 * All subsequent AgentX protocol operations initiated by the master
67 	 * agent for this session must use this byte ordering and set this bit
68 	 * accordingly.
69 	 */
70 	enum ax_byte_order sess_byteorder;
71 	uint8_t sess_timeout;
72 	struct ax_oid sess_oid;
73 	struct ax_ostring sess_descr;
74 	struct appl_backend sess_backend;
75 
76 	RB_ENTRY(appl_agentx_session) sess_entry;
77 	TAILQ_ENTRY(appl_agentx_session) sess_conn_entry;
78 };
79 
80 void appl_agentx_listen(struct agentx_master *);
81 void appl_agentx_accept(int, short, void *);
82 void appl_agentx_free(struct appl_agentx_connection *, enum appl_close_reason);
83 void appl_agentx_recv(int, short, void *);
84 void appl_agentx_open(struct appl_agentx_connection *, struct ax_pdu *);
85 void appl_agentx_close(struct appl_agentx_session *, struct ax_pdu *);
86 void appl_agentx_forceclose(struct appl_backend *, enum appl_close_reason);
87 void appl_agentx_session_free(struct appl_agentx_session *);
88 void appl_agentx_register(struct appl_agentx_session *, struct ax_pdu *);
89 void appl_agentx_unregister(struct appl_agentx_session *, struct ax_pdu *);
90 void appl_agentx_get(struct appl_backend *, int32_t, int32_t, const char *,
91     struct appl_varbind *);
92 void appl_agentx_getnext(struct appl_backend *, int32_t, int32_t, const char *,
93     struct appl_varbind *);
94 void appl_agentx_addagentcaps(struct appl_agentx_session *, struct ax_pdu *);
95 void appl_agentx_removeagentcaps(struct appl_agentx_session *, struct ax_pdu *);
96 void appl_agentx_response(struct appl_agentx_session *, struct ax_pdu *);
97 void appl_agentx_send(int, short, void *);
98 struct ber_oid *appl_agentx_oid2ber_oid(struct ax_oid *, struct ber_oid *);
99 struct ber_element *appl_agentx_value2ber_element(struct ax_varbind *);
100 struct ax_ostring *appl_agentx_string2ostring(const char *,
101     struct ax_ostring *);
102 int appl_agentx_cmp(struct appl_agentx_connection *,
103     struct appl_agentx_connection *);
104 int appl_agentx_session_cmp(struct appl_agentx_session *,
105     struct appl_agentx_session *);
106 
107 struct appl_backend_functions appl_agentx_functions = {
108 	.ab_close = appl_agentx_forceclose,
109 	.ab_get = appl_agentx_get,
110 	.ab_getnext = appl_agentx_getnext,
111 	.ab_getbulk = NULL, /* not properly supported in application.c and libagentx */
112 };
113 
114 RB_HEAD(appl_agentx_conns, appl_agentx_connection) appl_agentx_conns =
115     RB_INITIALIZER(&appl_agentx_conns);
116 RB_HEAD(appl_agentx_sessions, appl_agentx_session) appl_agentx_sessions =
117     RB_INITIALIZER(&appl_agentx_sessions);
118 
119 RB_PROTOTYPE_STATIC(appl_agentx_conns, appl_agentx_connection, conn_entry,
120     appl_agentx_cmp);
121 RB_PROTOTYPE_STATIC(appl_agentx_sessions, appl_agentx_session, sess_entry,
122     appl_agentx_session_cmp);
123 
124 void
appl_agentx(void)125 appl_agentx(void)
126 {
127 	struct agentx_master *master;
128 
129 	TAILQ_FOREACH(master, &(snmpd_env->sc_agentx_masters), axm_entry)
130 		appl_agentx_listen(master);
131 }
132 
133 void
appl_agentx_init(void)134 appl_agentx_init(void)
135 {
136 	struct agentx_master *master;
137 
138 	TAILQ_FOREACH(master, &(snmpd_env->sc_agentx_masters), axm_entry) {
139 		if (master->axm_fd == -1)
140 			continue;
141 		event_set(&(master->axm_ev), master->axm_fd,
142 		    EV_READ | EV_PERSIST, appl_agentx_accept, master);
143 		event_add(&(master->axm_ev), NULL);
144 		log_info("AgentX: listening on %s", master->axm_sun.sun_path);
145 	}
146 }
147 void
appl_agentx_listen(struct agentx_master * master)148 appl_agentx_listen(struct agentx_master *master)
149 {
150 	mode_t mask;
151 
152 	unlink(master->axm_sun.sun_path);
153 
154 	mask = umask(0777);
155 	if ((master->axm_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ||
156 	    bind(master->axm_fd, (struct sockaddr *)&(master->axm_sun),
157 	    sizeof(master->axm_sun)) == -1 ||
158 	    listen(master->axm_fd, 5)) {
159 		log_warn("AgentX: listen %s", master->axm_sun.sun_path);
160 		umask(mask);
161 		return;
162 	}
163 	umask(mask);
164 	if (chown(master->axm_sun.sun_path, master->axm_owner,
165 	    master->axm_group) == -1) {
166 		log_warn("AgentX: chown %s", master->axm_sun.sun_path);
167 		goto fail;
168 	}
169 	if (chmod(master->axm_sun.sun_path, master->axm_mode) == -1) {
170 		log_warn("AgentX: chmod %s", master->axm_sun.sun_path);
171 		goto fail;
172 	}
173 	return;
174  fail:
175 	close(master->axm_fd);
176 	master->axm_fd = -1;
177 }
178 
179 void
appl_agentx_shutdown(void)180 appl_agentx_shutdown(void)
181 {
182 	struct appl_agentx_connection *conn, *tconn;
183 
184 	RB_FOREACH_SAFE(conn, appl_agentx_conns, &appl_agentx_conns, tconn)
185 		appl_agentx_free(conn, APPL_CLOSE_REASONSHUTDOWN);
186 }
187 
188 void
appl_agentx_accept(int masterfd,short event,void * cookie)189 appl_agentx_accept(int masterfd, short event, void *cookie)
190 {
191 	int fd;
192 	struct agentx_master *master = cookie;
193 	struct sockaddr_un sun;
194 	socklen_t sunlen = sizeof(sun);
195 	struct appl_agentx_connection *conn = NULL;
196 
197 	if ((fd = accept(masterfd, (struct sockaddr *)&sun, &sunlen)) == -1) {
198 		log_warn("AgentX: accept %s", master->axm_sun.sun_path);
199 		return;
200 	}
201 
202 	if ((conn = malloc(sizeof(*conn))) == NULL) {
203 		log_warn(NULL);
204 		goto fail;
205 	}
206 
207 	conn->conn_backend = 0;
208 	TAILQ_INIT(&(conn->conn_sessions));
209 	if ((conn->conn_ax = ax_new(fd)) == NULL) {
210 		log_warn(NULL);
211 		goto fail;
212 	}
213 
214 	do {
215 		conn->conn_id = arc4random();
216 	} while (RB_INSERT(appl_agentx_conns,
217 	    &appl_agentx_conns, conn) != NULL);
218 
219 	event_set(&(conn->conn_rev), fd, EV_READ | EV_PERSIST,
220 	    appl_agentx_recv, conn);
221 	event_add(&(conn->conn_rev), NULL);
222 	event_set(&(conn->conn_wev), fd, EV_WRITE, appl_agentx_send, conn);
223 	log_info("AgentX(%"PRIu32"): new connection", conn->conn_id);
224 
225 	return;
226  fail:
227 	close(fd);
228 	free(conn);
229 }
230 
231 void
appl_agentx_backend(int fd)232 appl_agentx_backend(int fd)
233 {
234 	struct appl_agentx_connection *conn;
235 
236 	if ((conn = malloc(sizeof(*conn))) == NULL)
237 		fatal(NULL);
238 
239 	conn->conn_backend = 1;
240 	TAILQ_INIT(&(conn->conn_sessions));
241 	if ((conn->conn_ax = ax_new(fd)) == NULL)
242 		fatal("ax_new");
243 
244 	do {
245 		conn->conn_id = arc4random();
246 	} while (RB_INSERT(appl_agentx_conns,
247 	    &appl_agentx_conns, conn) != NULL);
248 
249 	event_set(&(conn->conn_rev), fd, EV_READ | EV_PERSIST,
250 	    appl_agentx_recv, conn);
251 	event_add(&(conn->conn_rev), NULL);
252 	event_set(&(conn->conn_wev), fd, EV_WRITE, appl_agentx_send, conn);
253 }
254 
255 void
appl_agentx_free(struct appl_agentx_connection * conn,enum appl_close_reason reason)256 appl_agentx_free(struct appl_agentx_connection *conn,
257     enum appl_close_reason reason)
258 {
259 	struct appl_agentx_session *session;
260 
261 	while ((session = TAILQ_FIRST(&(conn->conn_sessions))) != NULL) {
262 		if (conn->conn_ax == NULL)
263 			appl_agentx_session_free(session);
264 		else
265 			appl_agentx_forceclose(&(session->sess_backend),
266 			    reason);
267 	}
268 
269 	event_del(&(conn->conn_rev));
270 	event_del(&(conn->conn_wev));
271 
272 	RB_REMOVE(appl_agentx_conns, &appl_agentx_conns, conn);
273 	if (conn->conn_ax != NULL)
274 		(void)ax_send(conn->conn_ax);
275 	ax_free(conn->conn_ax);
276 	if (conn->conn_backend)
277 		fatalx("AgentX(%"PRIu32"): disappeared unexpected",
278 		    conn->conn_id);
279 	free(conn);
280 }
281 
282 void
appl_agentx_recv(int fd,short event,void * cookie)283 appl_agentx_recv(int fd, short event, void *cookie)
284 {
285 	struct appl_agentx_connection *conn = cookie;
286 	struct appl_agentx_session *session = NULL;
287 	struct ax_pdu *pdu;
288 	enum appl_error error;
289 	char name[100];
290 
291 	snprintf(name, sizeof(name), "AgentX(%"PRIu32")", conn->conn_id);
292 	if ((pdu = ax_recv(conn->conn_ax)) == NULL) {
293 		if (errno == EAGAIN)
294 			return;
295 		log_warn("%s", name);
296 		/*
297 		 * Either the connection is dead, or we had garbage on the line.
298 		 * Both make sure we can't continue on this stream.
299 		 */
300 		if (errno == ECONNRESET) {
301 			ax_free(conn->conn_ax);
302 			conn->conn_ax = NULL;
303 		}
304 		appl_agentx_free(conn, errno == EPROTO ?
305 		    APPL_CLOSE_REASONPROTOCOLERROR : APPL_CLOSE_REASONOTHER);
306 		return;
307 	}
308 
309 	conn->conn_ax->ax_byteorder = pdu->ap_header.aph_flags &
310 	    AX_PDU_FLAG_NETWORK_BYTE_ORDER ?
311 	    AX_BYTE_ORDER_BE : AX_BYTE_ORDER_LE;
312 	if (pdu->ap_header.aph_type != AX_PDU_TYPE_OPEN) {
313 		/* Make sure we only look for connection-local sessions */
314 		TAILQ_FOREACH(session, &(conn->conn_sessions),
315 		    sess_conn_entry) {
316 			if (session->sess_id == pdu->ap_header.aph_sessionid)
317 				break;
318 		}
319 		if (session == NULL) {
320 			log_warnx("%s: Session %"PRIu32" not found for request",
321 			    name, pdu->ap_header.aph_sessionid);
322 			error = APPL_ERROR_NOTOPEN;
323 			goto fail;
324 		}
325 		strlcpy(name, session->sess_backend.ab_name, sizeof(name));
326 		/*
327 		 * RFC2741 section 7.1.1 bullet 4 is unclear on what byte order
328 		 * the response should be. My best guess is that it makes more
329 		 * sense that replies are in the same byte-order as what was
330 		 * requested.
331 		 * In practice we always have the same byte order as when we
332 		 * opened the session, so it's likely a non-issue, however, we
333 		 * can change to session byte order here.
334 		 */
335 	}
336 
337 	if (pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION) {
338 		if (pdu->ap_header.aph_type != AX_PDU_TYPE_REGISTER) {
339 			log_warnx("%s: %s: Invalid INSTANCE_REGISTRATION flag",
340 			    name, ax_pdutype2string(pdu->ap_header.aph_flags));
341 			error = APPL_ERROR_PARSEERROR;
342 			goto fail;
343 		}
344 	}
345 	if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NEW_INDEX) {
346 		if (pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
347 		    pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE) {
348 			log_warnx("%s: %s: Invalid NEW_INDEX flag", name,
349 			    ax_pdutype2string(pdu->ap_header.aph_flags));
350 			error = APPL_ERROR_PARSEERROR;
351 			goto fail;
352 		}
353 	}
354 	if (pdu->ap_header.aph_flags & AX_PDU_FLAG_ANY_INDEX) {
355 		if (pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
356 		    pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE) {
357 			log_warnx("%s: %s: Invalid ANY_INDEX flag", name,
358 			    ax_pdutype2string(pdu->ap_header.aph_flags));
359 			error = APPL_ERROR_PARSEERROR;
360 			goto fail;
361 		}
362 	}
363 	if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT) {
364 		if (pdu->ap_header.aph_type != AX_PDU_TYPE_REGISTER &&
365 		    pdu->ap_header.aph_type != AX_PDU_TYPE_UNREGISTER &&
366 		    pdu->ap_header.aph_type != AX_PDU_TYPE_ADDAGENTCAPS &&
367 		    pdu->ap_header.aph_type != AX_PDU_TYPE_REMOVEAGENTCAPS &&
368 		    pdu->ap_header.aph_type != AX_PDU_TYPE_GET &&
369 		    pdu->ap_header.aph_type != AX_PDU_TYPE_GETNEXT &&
370 		    pdu->ap_header.aph_type != AX_PDU_TYPE_GETBULK &&
371 		    pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
372 		    pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE &&
373 		    pdu->ap_header.aph_type != AX_PDU_TYPE_NOTIFY &&
374 		    pdu->ap_header.aph_type != AX_PDU_TYPE_TESTSET &&
375 		    pdu->ap_header.aph_type != AX_PDU_TYPE_PING) {
376 			log_warnx("%s: %s: Invalid NON_DEFAULT_CONTEXT flag",
377 			    name, ax_pdutype2string(pdu->ap_header.aph_flags));
378 			error = APPL_ERROR_PARSEERROR;
379 			goto fail;
380 		}
381 		if (appl_context(pdu->ap_context.aos_string, 0) == NULL) {
382 			log_warnx("%s: %s: Unsupported context",
383 			    name, ax_pdutype2string(pdu->ap_header.aph_flags));
384 			error = APPL_ERROR_UNSUPPORTEDCONTEXT;
385 			goto fail;
386 		}
387 	}
388 	switch (pdu->ap_header.aph_type) {
389 	case AX_PDU_TYPE_OPEN:
390 		appl_agentx_open(conn, pdu);
391 		break;
392 	case AX_PDU_TYPE_CLOSE:
393 		appl_agentx_close(session, pdu);
394 		break;
395 	case AX_PDU_TYPE_REGISTER:
396 		appl_agentx_register(session, pdu);
397 		break;
398 	case AX_PDU_TYPE_UNREGISTER:
399 		appl_agentx_unregister(session, pdu);
400 		break;
401 	case AX_PDU_TYPE_GET:
402 	case AX_PDU_TYPE_GETNEXT:
403 	case AX_PDU_TYPE_GETBULK:
404 	case AX_PDU_TYPE_TESTSET:
405 	case AX_PDU_TYPE_COMMITSET:
406 	case AX_PDU_TYPE_UNDOSET:
407 	case AX_PDU_TYPE_CLEANUPSET:
408 		log_warnx("%s: %s: Not an adminsitrative message", name,
409 		    ax_pdutype2string(pdu->ap_header.aph_type));
410 		error = APPL_ERROR_PARSEERROR;
411 		goto fail;
412 	case AX_PDU_TYPE_NOTIFY:
413 		log_warnx("%s: %s: not supported", name,
414 		    ax_pdutype2string(pdu->ap_header.aph_type));
415 		/*
416 		 * RFC 2741 section 7.1.10:
417 		 * Note that the master agent's successful response indicates
418 		 * the agentx-Notify-PDU was received and validated.  It does
419 		 * not indicate that any particular notifications were actually
420 		 * generated or received by notification targets
421 		 */
422 		/* XXX Not yet - FALLTHROUGH */
423 	case AX_PDU_TYPE_PING:
424 		ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
425 		    pdu->ap_header.aph_transactionid,
426 		    pdu->ap_header.aph_packetid, smi_getticks(),
427 		    APPL_ERROR_NOERROR, 0, NULL, 0);
428 		event_add(&(conn->conn_wev), NULL);
429 		break;
430 	case AX_PDU_TYPE_INDEXALLOCATE:
431 	case AX_PDU_TYPE_INDEXDEALLOCATE:
432 		log_warnx("%s: %s: not supported", name,
433 		    ax_pdutype2string(pdu->ap_header.aph_type));
434 		ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
435 		    pdu->ap_header.aph_transactionid,
436 		    pdu->ap_header.aph_packetid, smi_getticks(),
437 		    APPL_ERROR_PROCESSINGERROR, 1,
438 		    pdu->ap_payload.ap_vbl.ap_varbind,
439 		    pdu->ap_payload.ap_vbl.ap_nvarbind);
440 		event_add(&(conn->conn_wev), NULL);
441 		break;
442 	case AX_PDU_TYPE_ADDAGENTCAPS:
443 		appl_agentx_addagentcaps(session, pdu);
444 		break;
445 	case AX_PDU_TYPE_REMOVEAGENTCAPS:
446 		appl_agentx_removeagentcaps(session, pdu);
447 		break;
448 	case AX_PDU_TYPE_RESPONSE:
449 		appl_agentx_response(session, pdu);
450 		break;
451 	}
452 
453 	ax_pdu_free(pdu);
454 	return;
455  fail:
456 	ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
457 	    pdu->ap_header.aph_transactionid,
458 	    pdu->ap_header.aph_packetid, smi_getticks(),
459 	    error, 0, NULL, 0);
460 	event_add(&(conn->conn_wev), NULL);
461 	ax_pdu_free(pdu);
462 
463 	if (session == NULL || error != APPL_ERROR_PARSEERROR)
464 		return;
465 
466 	appl_agentx_forceclose(&(session->sess_backend),
467 	    APPL_CLOSE_REASONPARSEERROR);
468 	if (TAILQ_EMPTY(&(conn->conn_sessions)))
469 		appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
470 }
471 
472 void
appl_agentx_open(struct appl_agentx_connection * conn,struct ax_pdu * pdu)473 appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu)
474 {
475 	struct appl_agentx_session *session;
476 	struct ber_oid oid;
477 	char oidbuf[1024];
478 	enum appl_error error = APPL_ERROR_NOERROR;
479 
480 	if ((session = malloc(sizeof(*session))) == NULL) {
481 		log_warn(NULL);
482 		error = APPL_ERROR_OPENFAILED;
483 		goto fail;
484 	}
485 	session->sess_descr.aos_string = NULL;
486 
487 	session->sess_conn = conn;
488 	if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
489 		session->sess_byteorder = AX_BYTE_ORDER_BE;
490 	else
491 		session->sess_byteorder = AX_BYTE_ORDER_LE;
492 
493 	/* RFC 2742 agentxSessionObjectID */
494 	if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 0) {
495 		pdu->ap_payload.ap_open.ap_oid.aoi_id[0] = 0;
496 		pdu->ap_payload.ap_open.ap_oid.aoi_id[1] = 0;
497 		pdu->ap_payload.ap_open.ap_oid.aoi_idlen = 2;
498 	} else if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 1) {
499 		log_warnx("AgentX(%"PRIu32"): Invalid oid: Open Failed",
500 		    conn->conn_id);
501 		error = APPL_ERROR_PARSEERROR;
502 		goto fail;
503 	}
504 	/* RFC 2742 agentxSessionDescr */
505 	if (pdu->ap_payload.ap_open.ap_descr.aos_slen > 255) {
506 		log_warnx("AgentX(%"PRIu32"): Invalid descr (too long): Open "
507 		    "Failed", conn->conn_id);
508 		error = APPL_ERROR_PARSEERROR;
509 		goto fail;
510 	}
511 	/*
512 	 * ax_ostring is always NUL-terminated, but doesn't scan for internal
513 	 * NUL-bytes. However, mbstowcs stops at NUL, which might be in the
514 	 * middle of the string.
515 	 */
516 	if (strlen(pdu->ap_payload.ap_open.ap_descr.aos_string) !=
517 	    pdu->ap_payload.ap_open.ap_descr.aos_slen ||
518 	    mbstowcs(NULL,
519 	    pdu->ap_payload.ap_open.ap_descr.aos_string, 0) == (size_t)-1) {
520 		log_warnx("AgentX(%"PRIu32"): Invalid descr (not UTF-8): "
521 		    "Open Failed", conn->conn_id);
522 		error = APPL_ERROR_PARSEERROR;
523 		goto fail;
524 	}
525 
526 	session->sess_timeout = pdu->ap_payload.ap_open.ap_timeout;
527 	session->sess_oid = pdu->ap_payload.ap_open.ap_oid;
528 	session->sess_descr.aos_slen = pdu->ap_payload.ap_open.ap_descr.aos_slen;
529 	if (pdu->ap_payload.ap_open.ap_descr.aos_string != NULL) {
530 		session->sess_descr.aos_string =
531 		    strdup(pdu->ap_payload.ap_open.ap_descr.aos_string);
532 		if (session->sess_descr.aos_string == NULL) {
533 			log_warn("AgentX(%"PRIu32"): strdup: Open Failed",
534 			    conn->conn_id);
535 			error = APPL_ERROR_OPENFAILED;
536 			goto fail;
537 		}
538 	}
539 
540 	/* RFC 2742 agentxSessionIndex: chances of reuse, slim to none */
541 	do {
542 		session->sess_id = arc4random();
543 	} while (RB_INSERT(appl_agentx_sessions,
544 	    &appl_agentx_sessions, session) != NULL);
545 
546 	if (asprintf(&(session->sess_backend.ab_name),
547 	    "AgentX(%"PRIu32"/%"PRIu32")",
548 	    conn->conn_id, session->sess_id) == -1) {
549 		log_warn("AgentX(%"PRIu32"): asprintf: Open Failed",
550 		    conn->conn_id);
551 		error = APPL_ERROR_OPENFAILED;
552 		goto fail;
553 	}
554 	session->sess_backend.ab_cookie = session;
555 	session->sess_backend.ab_retries = 0;
556 	session->sess_backend.ab_fn = &appl_agentx_functions;
557 	session->sess_backend.ab_range = 1;
558 	RB_INIT(&(session->sess_backend.ab_requests));
559 	TAILQ_INSERT_TAIL(&(conn->conn_sessions), session, sess_conn_entry);
560 
561 	appl_agentx_oid2ber_oid(&(session->sess_oid), &oid);
562 	mib_oid2string(&oid, oidbuf, sizeof(oidbuf), snmpd_env->sc_oidfmt);
563 	log_info("%s: %s %s: Open", session->sess_backend.ab_name, oidbuf,
564 	    session->sess_descr.aos_string);
565 
566 	ax_response(conn->conn_ax, session->sess_id,
567 	    pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
568 	    smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
569 	event_add(&(conn->conn_wev), NULL);
570 
571 	return;
572  fail:
573 	ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid,
574 	    pdu->ap_header.aph_packetid, 0, error, 0, NULL, 0);
575 	event_add(&(conn->conn_wev), NULL);
576 	if (session != NULL)
577 		free(session->sess_descr.aos_string);
578 	free(session);
579 }
580 
581 void
appl_agentx_close(struct appl_agentx_session * session,struct ax_pdu * pdu)582 appl_agentx_close(struct appl_agentx_session *session, struct ax_pdu *pdu)
583 {
584 	struct appl_agentx_connection *conn = session->sess_conn;
585 	char name[100];
586 	enum appl_error error = APPL_ERROR_NOERROR;
587 
588 	strlcpy(name, session->sess_backend.ab_name, sizeof(name));
589 	if (pdu->ap_payload.ap_close.ap_reason == AX_CLOSE_BYMANAGER) {
590 		log_warnx("%s: Invalid close reason", name);
591 		error = APPL_ERROR_PARSEERROR;
592 	} else {
593 		appl_agentx_session_free(session);
594 		log_info("%s: Closed by subagent (%s)", name,
595 		    ax_closereason2string(pdu->ap_payload.ap_close.ap_reason));
596 	}
597 
598 	ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
599 	    pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
600 	    smi_getticks(), error, 0, NULL, 0);
601 	event_add(&(conn->conn_wev), NULL);
602 	if (error == APPL_ERROR_NOERROR)
603 		return;
604 
605 	appl_agentx_forceclose(&(session->sess_backend),
606 	    APPL_CLOSE_REASONPARSEERROR);
607 	if (TAILQ_EMPTY(&(conn->conn_sessions)))
608 		appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
609 }
610 
611 void
appl_agentx_forceclose(struct appl_backend * backend,enum appl_close_reason reason)612 appl_agentx_forceclose(struct appl_backend *backend,
613     enum appl_close_reason reason)
614 {
615 	struct appl_agentx_session *session = backend->ab_cookie;
616 	char name[100];
617 
618 	session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
619 	ax_close(session->sess_conn->conn_ax, session->sess_id,
620 	    (enum ax_close_reason) reason);
621 	event_add(&(session->sess_conn->conn_wev), NULL);
622 
623 	strlcpy(name, session->sess_backend.ab_name, sizeof(name));
624 	appl_agentx_session_free(session);
625 	log_info("%s: Closed by snmpd (%s)", name,
626 	    ax_closereason2string((enum ax_close_reason)reason));
627 }
628 
629 void
appl_agentx_session_free(struct appl_agentx_session * session)630 appl_agentx_session_free(struct appl_agentx_session *session)
631 {
632 	struct appl_agentx_connection *conn = session->sess_conn;
633 
634 	appl_close(&(session->sess_backend));
635 
636 	RB_REMOVE(appl_agentx_sessions, &appl_agentx_sessions, session);
637 	TAILQ_REMOVE(&(conn->conn_sessions), session, sess_conn_entry);
638 
639 	free(session->sess_backend.ab_name);
640 	free(session->sess_descr.aos_string);
641 	free(session);
642 }
643 
644 void
appl_agentx_register(struct appl_agentx_session * session,struct ax_pdu * pdu)645 appl_agentx_register(struct appl_agentx_session *session, struct ax_pdu *pdu)
646 {
647 	uint32_t timeout;
648 	struct ber_oid oid;
649 	enum appl_error error;
650 	int subtree = 0;
651 
652 	timeout = pdu->ap_payload.ap_register.ap_timeout;
653 	timeout = timeout != 0 ? timeout : session->sess_timeout != 0 ?
654 	    session->sess_timeout : AGENTX_DEFAULTTIMEOUT;
655 	timeout *= 100;
656 
657 	if (session->sess_conn->conn_backend) {
658 		pdu->ap_payload.ap_register.ap_priority = 1;
659 		subtree = 1;
660 	}
661 	if (appl_agentx_oid2ber_oid(
662 	    &(pdu->ap_payload.ap_register.ap_subtree), &oid) == NULL) {
663 		log_warnx("%s: Failed to register: oid too small",
664 		    session->sess_backend.ab_name);
665 		error = APPL_ERROR_PROCESSINGERROR;
666 		goto fail;
667 	}
668 
669 	error = appl_register(pdu->ap_context.aos_string, timeout,
670 	    pdu->ap_payload.ap_register.ap_priority, &oid,
671 	    pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION,
672 	    subtree, pdu->ap_payload.ap_register.ap_range_subid,
673 	    pdu->ap_payload.ap_register.ap_upper_bound,
674 	    &(session->sess_backend));
675 
676  fail:
677 	ax_response(session->sess_conn->conn_ax, session->sess_id,
678 	    pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
679 	    smi_getticks(), error, 0, NULL, 0);
680 	event_add(&(session->sess_conn->conn_wev), NULL);
681 }
682 
683 void
appl_agentx_unregister(struct appl_agentx_session * session,struct ax_pdu * pdu)684 appl_agentx_unregister(struct appl_agentx_session *session, struct ax_pdu *pdu)
685 {
686 	struct ber_oid oid;
687 	enum appl_error error;
688 
689 	if (appl_agentx_oid2ber_oid(
690 	    &(pdu->ap_payload.ap_unregister.ap_subtree), &oid) == NULL) {
691 		log_warnx("%s: Failed to unregister: oid too small",
692 		    session->sess_backend.ab_name);
693 		error = APPL_ERROR_PROCESSINGERROR;
694 		goto fail;
695 	}
696 
697 	error = appl_unregister(pdu->ap_context.aos_string,
698 	    pdu->ap_payload.ap_unregister.ap_priority, &oid,
699 	    pdu->ap_payload.ap_unregister.ap_range_subid,
700 	    pdu->ap_payload.ap_unregister.ap_upper_bound,
701 	    &(session->sess_backend));
702 
703  fail:
704 	ax_response(session->sess_conn->conn_ax, session->sess_id,
705 	    pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
706 	    smi_getticks(), error, 0, NULL, 0);
707 	event_add(&(session->sess_conn->conn_wev), NULL);
708 }
709 
710 #define AX_PDU_FLAG_INDEX (AX_PDU_FLAG_NEW_INDEX | AX_PDU_FLAG_ANY_INDEX)
711 
712 void
appl_agentx_get(struct appl_backend * backend,int32_t transactionid,int32_t requestid,const char * ctx,struct appl_varbind * vblist)713 appl_agentx_get(struct appl_backend *backend, int32_t transactionid,
714     int32_t requestid, const char *ctx, struct appl_varbind *vblist)
715 {
716 	struct appl_agentx_session *session = backend->ab_cookie;
717 	struct ax_ostring *context, string;
718 	struct appl_varbind *vb;
719 	struct ax_searchrange *srl;
720 	size_t i, j, nsr;
721 
722 	if (session->sess_conn->conn_ax == NULL)
723 		return;
724 
725 	for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
726 		nsr++;
727 
728 	if ((srl = calloc(nsr, sizeof(*srl))) == NULL) {
729 		log_warn(NULL);
730 		appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
731 		return;
732 	}
733 
734 	for (i = 0, vb = vblist; i < nsr; i++, vb = vb->av_next) {
735 		srl[i].asr_start.aoi_include = vb->av_include;
736 		srl[i].asr_start.aoi_idlen = vb->av_oid.bo_n;
737 		for (j = 0; j < vb->av_oid.bo_n; j++)
738 			srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j];
739 		srl[i].asr_stop.aoi_include = 0;
740 		srl[i].asr_stop.aoi_idlen = 0;
741 	}
742 	if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {
743 		if (errno != 0) {
744 			log_warn("Failed to convert context");
745 			appl_response(backend, requestid,
746 			    APPL_ERROR_GENERR, 1, vblist);
747 			free(srl);
748 			return;
749 		}
750 	}
751 
752 	session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
753 	if (ax_get(session->sess_conn->conn_ax, session->sess_id, transactionid,
754 	    requestid, context, srl, nsr) == -1)
755 		appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
756 	else
757 		event_add(&(session->sess_conn->conn_wev), NULL);
758 	free(srl);
759 	if (context != NULL)
760 		free(context->aos_string);
761 }
762 
763 void
appl_agentx_getnext(struct appl_backend * backend,int32_t transactionid,int32_t requestid,const char * ctx,struct appl_varbind * vblist)764 appl_agentx_getnext(struct appl_backend *backend, int32_t transactionid,
765     int32_t requestid, const char *ctx, struct appl_varbind *vblist)
766 {
767 	struct appl_agentx_session *session = backend->ab_cookie;
768 	struct ax_ostring *context, string;
769 	struct appl_varbind *vb;
770 	struct ax_searchrange *srl;
771 	size_t i, j, nsr;
772 
773 	if (session->sess_conn->conn_ax == NULL)
774 		return;
775 
776 	for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
777 		nsr++;
778 
779 	if ((srl = calloc(nsr, sizeof(*srl))) == NULL) {
780 		log_warn(NULL);
781 		appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
782 		return;
783 	}
784 
785 	for (i = 0, vb = vblist; i < nsr; i++, vb = vb->av_next) {
786 		srl[i].asr_start.aoi_include = vb->av_include;
787 		srl[i].asr_start.aoi_idlen = vb->av_oid.bo_n;
788 		for (j = 0; j < vb->av_oid.bo_n; j++)
789 			srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j];
790 		srl[i].asr_stop.aoi_include = 0;
791 		srl[i].asr_stop.aoi_idlen = vb->av_oid_end.bo_n;
792 		for (j = 0; j < vb->av_oid_end.bo_n; j++)
793 			srl[i].asr_stop.aoi_id[j] = vb->av_oid_end.bo_id[j];
794 	}
795 	if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {
796 		if (errno != 0) {
797 			log_warn("Failed to convert context");
798 			appl_response(backend, requestid,
799 			    APPL_ERROR_GENERR, 1, vblist);
800 			free(srl);
801 			return;
802 		}
803 	}
804 
805 	session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
806 	if (ax_getnext(session->sess_conn->conn_ax, session->sess_id, transactionid,
807 	    requestid, context, srl, nsr) == -1)
808 		appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
809 	else
810 		event_add(&(session->sess_conn->conn_wev), NULL);
811 	free(srl);
812 	if (context != NULL)
813 		free(context->aos_string);
814 }
815 
816 void
appl_agentx_addagentcaps(struct appl_agentx_session * session,struct ax_pdu * pdu)817 appl_agentx_addagentcaps(struct appl_agentx_session *session,
818     struct ax_pdu *pdu)
819 {
820 	struct ber_oid oid;
821 	enum appl_error error;
822 
823 	if (appl_agentx_oid2ber_oid(&(pdu->ap_payload.ap_addagentcaps.ap_oid),
824 	    &oid) == NULL) {
825 		log_warnx("%s: Failed to add agent capabilities: oid too small",
826 		    session->sess_backend.ab_name);
827 		error = APPL_ERROR_PARSEERROR;
828 		goto fail;
829 	}
830 
831 	error = appl_addagentcaps(pdu->ap_context.aos_string, &oid,
832 	    pdu->ap_payload.ap_addagentcaps.ap_descr.aos_string,
833 	    &(session->sess_backend));
834 
835  fail:
836 	ax_response(session->sess_conn->conn_ax, session->sess_id,
837 	    pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
838 	    smi_getticks(), error, 0, NULL, 0);
839 	event_add(&(session->sess_conn->conn_wev), NULL);
840 }
841 
842 void
appl_agentx_removeagentcaps(struct appl_agentx_session * session,struct ax_pdu * pdu)843 appl_agentx_removeagentcaps(struct appl_agentx_session *session,
844     struct ax_pdu *pdu)
845 {
846 	struct ber_oid oid;
847 	enum appl_error error;
848 
849 	if (appl_agentx_oid2ber_oid(&(pdu->ap_payload.ap_addagentcaps.ap_oid),
850 	    &oid) == NULL) {
851 		log_warnx("%s: Failed to remove agent capabilities: "
852 		    "oid too small", session->sess_backend.ab_name);
853 		error = APPL_ERROR_PARSEERROR;
854 		goto fail;
855 	}
856 
857 	error = appl_removeagentcaps(pdu->ap_context.aos_string, &oid,
858 	    &(session->sess_backend));
859 
860  fail:
861 	ax_response(session->sess_conn->conn_ax, session->sess_id,
862 	    pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
863 	    smi_getticks(), error, 0, NULL, 0);
864 	event_add(&(session->sess_conn->conn_wev), NULL);
865 }
866 
867 void
appl_agentx_response(struct appl_agentx_session * session,struct ax_pdu * pdu)868 appl_agentx_response(struct appl_agentx_session *session, struct ax_pdu *pdu)
869 {
870 	struct appl_varbind *response = NULL;
871 	struct ax_varbind *vb;
872 	enum appl_error error;
873 	uint16_t index;
874 	size_t i, nvarbind;
875 
876 	nvarbind = pdu->ap_payload.ap_response.ap_nvarbind;
877 	if ((response = calloc(nvarbind, sizeof(*response))) == NULL) {
878 		log_warn(NULL);
879 		appl_response(&(session->sess_backend),
880 		    pdu->ap_header.aph_packetid,
881 		    APPL_ERROR_GENERR, 1, NULL);
882 		return;
883 	}
884 
885 	error = (enum appl_error)pdu->ap_payload.ap_response.ap_error;
886 	index = pdu->ap_payload.ap_response.ap_index;
887 	for (i = 0; i < nvarbind; i++) {
888 		response[i].av_next = i + 1 == nvarbind ?
889 		    NULL : &(response[i + 1]);
890 		vb = &(pdu->ap_payload.ap_response.ap_varbindlist[i]);
891 
892 		if (appl_agentx_oid2ber_oid(&(vb->avb_oid),
893 		    &(response[i].av_oid)) == NULL) {
894 			log_warnx("%s: invalid oid",
895 			    session->sess_backend.ab_name);
896 			if (error != APPL_ERROR_NOERROR) {
897 				error = APPL_ERROR_GENERR;
898 				index = i + 1;
899 			}
900 			continue;
901 		}
902 		response[i].av_value = appl_agentx_value2ber_element(vb);
903 		if (response[i].av_value == NULL) {
904 			log_warn("%s: Failed to parse response value",
905 			    session->sess_backend.ab_name);
906 			if (error != APPL_ERROR_NOERROR) {
907 				error = APPL_ERROR_GENERR;
908 				index = i + 1;
909 			}
910 		}
911 	}
912 	appl_response(&(session->sess_backend), pdu->ap_header.aph_packetid,
913 	    error, index, response);
914 	free(response);
915 }
916 
917 void
appl_agentx_send(int fd,short event,void * cookie)918 appl_agentx_send(int fd, short event, void *cookie)
919 {
920 	struct appl_agentx_connection *conn = cookie;
921 
922 	switch (ax_send(conn->conn_ax)) {
923 	case -1:
924 		if (errno == EAGAIN)
925 			break;
926 		log_warn("AgentX(%"PRIu32")", conn->conn_id);
927 		ax_free(conn->conn_ax);
928 		conn->conn_ax = NULL;
929 		appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
930 		return;
931 	case 0:
932 		return;
933 	default:
934 		break;
935 	}
936 	event_add(&(conn->conn_wev), NULL);
937 }
938 
939 struct ber_oid *
appl_agentx_oid2ber_oid(struct ax_oid * aoid,struct ber_oid * boid)940 appl_agentx_oid2ber_oid(struct ax_oid *aoid, struct ber_oid *boid)
941 {
942 	size_t i;
943 
944 	if (aoid->aoi_idlen < BER_MIN_OID_LEN ||
945 	    aoid->aoi_idlen > BER_MAX_OID_LEN) {
946 		errno = EINVAL;
947 		return NULL;
948 	}
949 
950 
951 	boid->bo_n = aoid->aoi_idlen;
952 	for (i = 0; i < boid->bo_n; i++)
953 		boid->bo_id[i] = aoid->aoi_id[i];
954 	return boid;
955 }
956 
957 struct ber_element *
appl_agentx_value2ber_element(struct ax_varbind * vb)958 appl_agentx_value2ber_element(struct ax_varbind *vb)
959 {
960 	struct ber_oid oid;
961 	struct ber_element *elm;
962 
963 	switch (vb->avb_type) {
964 	case AX_DATA_TYPE_INTEGER:
965 		return ober_add_integer(NULL, vb->avb_data.avb_int32);
966 	case AX_DATA_TYPE_OCTETSTRING:
967 		return ober_add_nstring(NULL,
968 		    vb->avb_data.avb_ostring.aos_string,
969 		    vb->avb_data.avb_ostring.aos_slen);
970 	case AX_DATA_TYPE_NULL:
971 		return ober_add_null(NULL);
972 	case AX_DATA_TYPE_OID:
973 		if (appl_agentx_oid2ber_oid(
974 		    &(vb->avb_data.avb_oid), &oid) == NULL)
975 			return NULL;
976 		return ober_add_oid(NULL, &oid);
977 	case AX_DATA_TYPE_IPADDRESS:
978 		if ((elm = ober_add_nstring(NULL,
979 		    vb->avb_data.avb_ostring.aos_string,
980 		    vb->avb_data.avb_ostring.aos_slen)) == NULL)
981 			return NULL;
982 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_IPADDR);
983 		return elm;
984 	case AX_DATA_TYPE_COUNTER32:
985 		elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
986 		if (elm == NULL)
987 			return NULL;
988 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
989 		return elm;
990 	case AX_DATA_TYPE_GAUGE32:
991 		elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
992 		if (elm == NULL)
993 			return NULL;
994 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_GAUGE32);
995 		return elm;
996 	case AX_DATA_TYPE_TIMETICKS:
997 		elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
998 		if (elm == NULL)
999 			return NULL;
1000 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
1001 		return elm;
1002 	case AX_DATA_TYPE_OPAQUE:
1003 		if ((elm = ober_add_nstring(NULL,
1004 		    vb->avb_data.avb_ostring.aos_string,
1005 		    vb->avb_data.avb_ostring.aos_slen)) == NULL)
1006 			return NULL;
1007 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_OPAQUE);
1008 		return elm;
1009 	case AX_DATA_TYPE_COUNTER64:
1010 		elm = ober_add_integer(NULL, vb->avb_data.avb_uint64);
1011 		if (elm == NULL)
1012 			return NULL;
1013 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
1014 		return elm;
1015 	case AX_DATA_TYPE_NOSUCHOBJECT:
1016 		return appl_exception(APPL_EXC_NOSUCHOBJECT);
1017 	case AX_DATA_TYPE_NOSUCHINSTANCE:
1018 		return appl_exception(APPL_EXC_NOSUCHINSTANCE);
1019 	case AX_DATA_TYPE_ENDOFMIBVIEW:
1020 		return appl_exception(APPL_EXC_ENDOFMIBVIEW);
1021 	default:
1022 		errno = EINVAL;
1023 		return NULL;
1024 	}
1025 }
1026 
1027 struct ax_ostring *
appl_agentx_string2ostring(const char * str,struct ax_ostring * ostring)1028 appl_agentx_string2ostring(const char *str, struct ax_ostring *ostring)
1029 {
1030 	if (str == NULL) {
1031 		errno = 0;
1032 		return NULL;
1033 	}
1034 
1035 	ostring->aos_slen = strlen(str);
1036 	if ((ostring->aos_string = strdup(str)) == NULL)
1037 		return NULL;
1038 	return ostring;
1039 }
1040 
1041 int
appl_agentx_cmp(struct appl_agentx_connection * conn1,struct appl_agentx_connection * conn2)1042 appl_agentx_cmp(struct appl_agentx_connection *conn1,
1043     struct appl_agentx_connection *conn2)
1044 {
1045 	return conn1->conn_id < conn2->conn_id ? -1 :
1046 	    conn1->conn_id > conn2->conn_id;
1047 }
1048 
1049 int
appl_agentx_session_cmp(struct appl_agentx_session * sess1,struct appl_agentx_session * sess2)1050 appl_agentx_session_cmp(struct appl_agentx_session *sess1,
1051     struct appl_agentx_session *sess2)
1052 {
1053 	return sess1->sess_id < sess2->sess_id ? -1 : sess1->sess_id > sess2->sess_id;
1054 }
1055 
1056 RB_GENERATE_STATIC(appl_agentx_conns, appl_agentx_connection, conn_entry,
1057     appl_agentx_cmp);
1058 RB_GENERATE_STATIC(appl_agentx_sessions, appl_agentx_session, sess_entry,
1059     appl_agentx_session_cmp);
1060