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