1/* $OpenBSD: snmpe.c,v 1.95 2024/05/21 05:00:48 jsg Exp $ */ 2 3/* 4 * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org> 5 * Copyright (c) 2017 Marco Pfatschbacher <mpf@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <sys/queue.h> 21#include <sys/socket.h> 22#include <sys/time.h> 23#include <sys/tree.h> 24#include <sys/types.h> 25 26#include <netinet/in.h> 27 28#include <ber.h> 29#include <event.h> 30#include <errno.h> 31#include <fcntl.h> 32#include <imsg.h> 33#include <locale.h> 34#include <stdlib.h> 35#include <stdio.h> 36#include <string.h> 37#include <strings.h> 38#include <unistd.h> 39 40#include "application.h" 41#include "log.h" 42#include "snmpd.h" 43#include "snmpe.h" 44#include "mib.h" 45 46void snmpe_init(struct privsep *, struct privsep_proc *, void *); 47int snmpe_dispatch_parent(int, struct privsep_proc *, struct imsg *); 48int snmpe_parse(struct snmp_message *); 49void snmpe_tryparse(int, struct snmp_message *); 50int snmpe_parsevarbinds(struct snmp_message *); 51int snmpe_bind(struct address *); 52void snmpe_recvmsg(int fd, short, void *); 53void snmpe_readcb(int fd, short, void *); 54void snmpe_writecb(int fd, short, void *); 55void snmpe_acceptcb(int fd, short, void *); 56void snmpe_prepare_read(struct snmp_message *, int); 57int snmpe_encode(struct snmp_message *); 58 59struct imsgev *iev_parent; 60static const struct timeval snmpe_tcp_timeout = { 10, 0 }; /* 10s */ 61 62struct snmp_messages snmp_messages = RB_INITIALIZER(&snmp_messages); 63 64static struct privsep_proc procs[] = { 65 { "parent", PROC_PARENT, snmpe_dispatch_parent } 66}; 67 68void 69snmpe(struct privsep *ps, struct privsep_proc *p) 70{ 71 struct snmpd *env = ps->ps_env; 72 struct address *h; 73 74 if ((setlocale(LC_CTYPE, "en_US.UTF-8")) == NULL) 75 fatal("setlocale(LC_CTYPE, \"en_US.UTF-8\")"); 76 77 appl(); 78 79 /* bind SNMP UDP/TCP sockets */ 80 TAILQ_FOREACH(h, &env->sc_addresses, entry) 81 if ((h->fd = snmpe_bind(h)) == -1) 82 fatal("snmpe: failed to bind SNMP socket"); 83 84 proc_run(ps, p, procs, nitems(procs), snmpe_init, NULL); 85} 86 87void 88snmpe_init(struct privsep *ps, struct privsep_proc *p, void *arg) 89{ 90 struct snmpd *env = ps->ps_env; 91 struct address *h; 92 93 usm_generate_keys(); 94 appl_init(); 95 96 /* listen for incoming SNMP UDP/TCP messages */ 97 TAILQ_FOREACH(h, &env->sc_addresses, entry) { 98 if (h->type == SOCK_STREAM) { 99 if (listen(h->fd, 5) < 0) 100 fatalx("snmpe: failed to listen on socket"); 101 event_set(&h->ev, h->fd, EV_READ, snmpe_acceptcb, h); 102 evtimer_set(&h->evt, snmpe_acceptcb, h); 103 } else { 104 event_set(&h->ev, h->fd, EV_READ|EV_PERSIST, 105 snmpe_recvmsg, h); 106 } 107 event_add(&h->ev, NULL); 108 } 109 110 /* no filesystem visibility */ 111 if (unveil("/", "") == -1) 112 fatal("unveil /"); 113 if (pledge("stdio recvfd inet unix", NULL) == -1) 114 fatal("pledge"); 115 116 log_info("snmpe %s: ready", 117 tohexstr(env->sc_engineid, env->sc_engineid_len)); 118 trap_init(); 119} 120 121void 122snmpe_shutdown(void) 123{ 124 struct address *h; 125 126 TAILQ_FOREACH(h, &snmpd_env->sc_addresses, entry) { 127 event_del(&h->ev); 128 if (h->type == SOCK_STREAM) 129 event_del(&h->evt); 130 close(h->fd); 131 } 132 appl_shutdown(); 133} 134 135int 136snmpe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 137{ 138 switch (imsg->hdr.type) { 139 case IMSG_AX_FD: 140 appl_agentx_backend(imsg_get_fd(imsg)); 141 return 0; 142 default: 143 return -1; 144 } 145} 146 147int 148snmpe_bind(struct address *addr) 149{ 150 char buf[512]; 151 int val, s; 152 153 if ((s = snmpd_socket_af(&addr->ss, addr->type)) == -1) 154 return (-1); 155 156 /* 157 * Socket options 158 */ 159 if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) 160 goto bad; 161 162 if (addr->type == SOCK_STREAM) { 163 val = 1; 164 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 165 &val, sizeof(val)) == -1) 166 fatal("setsockopt SO_REUSEADDR"); 167 } else { /* UDP */ 168 switch (addr->ss.ss_family) { 169 case AF_INET: 170 val = 1; 171 if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, 172 &val, sizeof(int)) == -1) { 173 log_warn("%s: failed to set IPv4 packet info", 174 __func__); 175 goto bad; 176 } 177 break; 178 case AF_INET6: 179 val = 1; 180 if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, 181 &val, sizeof(int)) == -1) { 182 log_warn("%s: failed to set IPv6 packet info", 183 __func__); 184 goto bad; 185 } 186 } 187 } 188 189 if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1) 190 goto bad; 191 192 if (print_host(&addr->ss, buf, sizeof(buf)) == NULL) 193 goto bad; 194 195 log_info("snmpe: listening on %s %s:%d", 196 (addr->type == SOCK_STREAM) ? "tcp" : "udp", buf, addr->port); 197 198 return (s); 199 200 bad: 201 close(s); 202 return (-1); 203} 204 205const char * 206snmpe_pdutype2string(enum snmp_pdutype pdutype) 207{ 208 static char unknown[sizeof("Unknown (4294967295)")]; 209 210 switch (pdutype) { 211 case SNMP_C_GETREQ: 212 return "GetRequest"; 213 case SNMP_C_GETNEXTREQ: 214 return "GetNextRequest"; 215 case SNMP_C_RESPONSE: 216 return "Response"; 217 case SNMP_C_SETREQ: 218 return "SetRequest"; 219 case SNMP_C_TRAP: 220 return "Trap"; 221 case SNMP_C_GETBULKREQ: 222 return "GetBulkRequest"; 223 case SNMP_C_INFORMREQ: 224 return "InformRequest"; 225 case SNMP_C_TRAPV2: 226 return "SNMPv2-Trap"; 227 case SNMP_C_REPORT: 228 return "Report"; 229 } 230 231 snprintf(unknown, sizeof(unknown), "Unknown (%u)", pdutype); 232 return unknown; 233} 234 235int 236snmpe_parse(struct snmp_message *msg) 237{ 238 struct snmpd *env = snmpd_env; 239 struct snmp_stats *stats = &env->sc_stats; 240 struct ber_element *a; 241 long long ver, req; 242 long long errval, erridx; 243 u_int class; 244 char *comn; 245 char *flagstr, *ctxname, *engineid; 246 size_t len; 247 struct sockaddr_storage *ss = &msg->sm_ss; 248 struct ber_element *root = msg->sm_req; 249 250 msg->sm_errstr = "invalid message"; 251 252 do { 253 msg->sm_transactionid = arc4random(); 254 } while (msg->sm_transactionid == 0 || 255 RB_INSERT(snmp_messages, &snmp_messages, msg) != NULL); 256 257 if (ober_scanf_elements(root, "{ie", &ver, &a) != 0) 258 goto parsefail; 259 260 /* SNMP version and community */ 261 msg->sm_version = ver; 262 switch (msg->sm_version) { 263 case SNMP_V1: 264 if (!(msg->sm_aflags & ADDRESS_FLAG_SNMPV1)) { 265 msg->sm_errstr = "SNMPv1 disabled"; 266 goto badversion; 267 } 268 case SNMP_V2: 269 if (msg->sm_version == SNMP_V2 && 270 !(msg->sm_aflags & ADDRESS_FLAG_SNMPV2)) { 271 msg->sm_errstr = "SNMPv2c disabled"; 272 goto badversion; 273 } 274 if (ober_scanf_elements(a, "seS$", &comn, &msg->sm_pdu) != 0) 275 goto parsefail; 276 if (strlcpy(msg->sm_community, comn, 277 sizeof(msg->sm_community)) >= sizeof(msg->sm_community) || 278 msg->sm_community[0] == '\0') { 279 stats->snmp_inbadcommunitynames++; 280 msg->sm_errstr = "invalid community name"; 281 goto fail; 282 } 283 break; 284 case SNMP_V3: 285 if (!(msg->sm_aflags & ADDRESS_FLAG_SNMPV3)) { 286 msg->sm_errstr = "SNMPv3 disabled"; 287 goto badversion; 288 } 289 if (ober_scanf_elements(a, "{iisi$}e", 290 &msg->sm_msgid, &msg->sm_max_msg_size, &flagstr, 291 &msg->sm_secmodel, &a) != 0) 292 goto parsefail; 293 294 msg->sm_flags = *flagstr; 295 if ((a = usm_decode(msg, a, &msg->sm_errstr)) == NULL) 296 goto parsefail; 297 298 if (MSG_SECLEVEL(msg) < env->sc_min_seclevel || 299 msg->sm_secmodel != SNMP_SEC_USM) { 300 /* XXX currently only USM supported */ 301 msg->sm_errstr = "unsupported security model"; 302 stats->snmp_usmbadseclevel++; 303 msg->sm_usmerr = OIDVAL_usmErrSecLevel; 304 goto parsefail; 305 } 306 307 if (ober_scanf_elements(a, "{xxeS$}$", 308 &engineid, &msg->sm_ctxengineid_len, &ctxname, &len, 309 &msg->sm_pdu) != 0) 310 goto parsefail; 311 if (msg->sm_ctxengineid_len > sizeof(msg->sm_ctxengineid)) 312 goto parsefail; 313 memcpy(msg->sm_ctxengineid, engineid, msg->sm_ctxengineid_len); 314 if (len > SNMPD_MAXCONTEXNAMELEN) 315 goto parsefail; 316 memcpy(msg->sm_ctxname, ctxname, len); 317 msg->sm_ctxname[len] = '\0'; 318 break; 319 default: 320 msg->sm_errstr = "unsupported snmp version"; 321badversion: 322 stats->snmp_inbadversions++; 323 goto fail; 324 } 325 326 if (ober_scanf_elements(msg->sm_pdu, "t{e", &class, &(msg->sm_pdutype), 327 &a) != 0) 328 goto parsefail; 329 330 /* SNMP PDU context */ 331 if (class != BER_CLASS_CONTEXT) 332 goto parsefail; 333 334 switch (msg->sm_pdutype) { 335 case SNMP_C_GETBULKREQ: 336 if (msg->sm_version == SNMP_V1) { 337 stats->snmp_inbadversions++; 338 msg->sm_errstr = 339 "invalid request for protocol version 1"; 340 goto fail; 341 } 342 /* FALLTHROUGH */ 343 344 case SNMP_C_GETREQ: 345 stats->snmp_ingetrequests++; 346 /* FALLTHROUGH */ 347 348 case SNMP_C_GETNEXTREQ: 349 if (msg->sm_pdutype == SNMP_C_GETNEXTREQ) 350 stats->snmp_ingetnexts++; 351 if (!(msg->sm_aflags & ADDRESS_FLAG_READ)) { 352 msg->sm_errstr = "read requests disabled"; 353 goto fail; 354 } 355 if (msg->sm_version != SNMP_V3 && 356 strcmp(env->sc_rdcommunity, msg->sm_community) != 0 && 357 strcmp(env->sc_rwcommunity, msg->sm_community) != 0) { 358 stats->snmp_inbadcommunitynames++; 359 msg->sm_errstr = "wrong read community"; 360 goto fail; 361 } 362 break; 363 364 case SNMP_C_SETREQ: 365 stats->snmp_insetrequests++; 366 if (!(msg->sm_aflags & ADDRESS_FLAG_WRITE)) { 367 msg->sm_errstr = "write requests disabled"; 368 goto fail; 369 } 370 if (msg->sm_version != SNMP_V3 && 371 strcmp(env->sc_rwcommunity, msg->sm_community) != 0) { 372 if (strcmp(env->sc_rdcommunity, msg->sm_community) != 0) 373 stats->snmp_inbadcommunitynames++; 374 else 375 stats->snmp_inbadcommunityuses++; 376 msg->sm_errstr = "wrong write community"; 377 goto fail; 378 } 379 break; 380 381 case SNMP_C_RESPONSE: 382 stats->snmp_ingetresponses++; 383 msg->sm_errstr = "response without request"; 384 goto parsefail; 385 386 case SNMP_C_TRAP: 387 if (msg->sm_version != SNMP_V1) { 388 msg->sm_errstr = "trapv1 request on !SNMPv1 message"; 389 goto parsefail; 390 } 391 case SNMP_C_TRAPV2: 392 if (msg->sm_pdutype == SNMP_C_TRAPV2 && 393 !(msg->sm_version == SNMP_V2 || 394 msg->sm_version == SNMP_V3)) { 395 msg->sm_errstr = "trapv2 request on !SNMPv2C or " 396 "!SNMPv3 message"; 397 goto parsefail; 398 } 399 if (!(msg->sm_aflags & ADDRESS_FLAG_NOTIFY)) { 400 msg->sm_errstr = "notify requests disabled"; 401 goto fail; 402 } 403 if (msg->sm_version == SNMP_V3) { 404 msg->sm_errstr = "SNMPv3 doesn't support traps yet"; 405 goto fail; 406 } 407 if (strcmp(env->sc_trcommunity, msg->sm_community) != 0) { 408 stats->snmp_inbadcommunitynames++; 409 msg->sm_errstr = "wrong trap community"; 410 goto fail; 411 } 412 stats->snmp_intraps++; 413 /* 414 * This should probably go into parsevarbinds, but that's for a 415 * next refactor 416 */ 417 if (traphandler_parse(msg) == -1) 418 goto fail; 419 /* Shortcircuit */ 420 return 0; 421 default: 422 msg->sm_errstr = "invalid context"; 423 goto parsefail; 424 } 425 426 /* SNMP PDU */ 427 if (ober_scanf_elements(a, "iiie{e{}}$", 428 &req, &errval, &erridx, &msg->sm_pduend, 429 &msg->sm_varbind) != 0) { 430 stats->snmp_silentdrops++; 431 msg->sm_errstr = "invalid PDU"; 432 goto fail; 433 } 434 435 for (len = 0, a = msg->sm_varbind; a != NULL; a = a->be_next, len++) { 436 if (ober_scanf_elements(a, "{oS$}", NULL) == -1) 437 goto parsefail; 438 } 439 /* 440 * error-status == non-repeaters 441 * error-index == max-repetitions 442 */ 443 if (msg->sm_pdutype == SNMP_C_GETBULKREQ && 444 (errval < 0 || errval > (long long)len || 445 erridx < 1 || erridx > UINT16_MAX)) 446 goto parsefail; 447 448 msg->sm_request = req; 449 msg->sm_error = errval; 450 msg->sm_errorindex = erridx; 451 452 print_host(ss, msg->sm_host, sizeof(msg->sm_host)); 453 if (msg->sm_version == SNMP_V3) 454 log_debug("%s: %s:%hd: SNMPv3 pdutype %s, flags %#x, " 455 "secmodel %lld, user '%s', ctx-engine %s, ctx-name '%s', " 456 "request %lld", __func__, msg->sm_host, msg->sm_port, 457 snmpe_pdutype2string(msg->sm_pdutype), msg->sm_flags, 458 msg->sm_secmodel, msg->sm_username, 459 tohexstr(msg->sm_ctxengineid, msg->sm_ctxengineid_len), 460 msg->sm_ctxname, msg->sm_request); 461 else 462 log_debug("%s: %s:%hd: SNMPv%d '%s' pdutype %s request %lld", 463 __func__, msg->sm_host, msg->sm_port, msg->sm_version + 1, 464 msg->sm_community, snmpe_pdutype2string(msg->sm_pdutype), 465 msg->sm_request); 466 467 return (0); 468 469 parsefail: 470 stats->snmp_inasnparseerrs++; 471 fail: 472 print_host(ss, msg->sm_host, sizeof(msg->sm_host)); 473 log_debug("%s: %s:%hd: %s", __func__, msg->sm_host, msg->sm_port, 474 msg->sm_errstr); 475 return (-1); 476} 477 478int 479snmpe_parsevarbinds(struct snmp_message *msg) 480{ 481 appl_processpdu(msg, msg->sm_ctxname, msg->sm_version, msg->sm_pdu); 482 return 0; 483} 484 485void 486snmpe_acceptcb(int fd, short type, void *arg) 487{ 488 struct address *h = arg; 489 struct sockaddr_storage ss; 490 socklen_t len = sizeof(ss); 491 struct snmp_message *msg; 492 int afd; 493 494 event_add(&h->ev, NULL); 495 if ((type & EV_TIMEOUT)) 496 return; 497 498 if ((afd = accept4(fd, (struct sockaddr *)&ss, &len, 499 SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0) { 500 /* Pause accept if we are out of file descriptors */ 501 if (errno == ENFILE || errno == EMFILE) { 502 struct timeval evtpause = { 1, 0 }; 503 504 event_del(&h->ev); 505 evtimer_add(&h->evt, &evtpause); 506 } else if (errno != EAGAIN && errno != EINTR) 507 log_debug("%s: accept4", __func__); 508 return; 509 } 510 if ((msg = calloc(1, sizeof(*msg))) == NULL) 511 goto fail; 512 513 memcpy(&(msg->sm_ss), &ss, len); 514 msg->sm_slen = len; 515 msg->sm_aflags = h->flags; 516 msg->sm_port = h->port; 517 snmpe_prepare_read(msg, afd); 518 return; 519fail: 520 free(msg); 521 close(afd); 522 return; 523} 524 525void 526snmpe_prepare_read(struct snmp_message *msg, int fd) 527{ 528 msg->sm_sock = fd; 529 msg->sm_sock_tcp = 1; 530 event_del(&msg->sm_sockev); 531 event_set(&msg->sm_sockev, fd, EV_READ, 532 snmpe_readcb, msg); 533 event_add(&msg->sm_sockev, &snmpe_tcp_timeout); 534} 535 536void 537snmpe_tryparse(int fd, struct snmp_message *msg) 538{ 539 struct snmp_stats *stats = &snmpd_env->sc_stats; 540 541 ober_set_application(&msg->sm_ber, smi_application); 542 ober_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen); 543 msg->sm_req = ober_read_elements(&msg->sm_ber, NULL); 544 if (msg->sm_req == NULL) { 545 if (errno == ECANCELED) { 546 /* short read; try again */ 547 snmpe_prepare_read(msg, fd); 548 return; 549 } 550 goto fail; 551 } 552 553 if (snmpe_parse(msg) == -1) { 554 if (msg->sm_usmerr && MSG_REPORT(msg)) { 555 usm_make_report(msg); 556 return; 557 } else 558 goto fail; 559 } 560 stats->snmp_inpkts++; 561 562 snmpe_dispatchmsg(msg); 563 return; 564 fail: 565 snmp_msgfree(msg); 566 close(fd); 567} 568 569void 570snmpe_readcb(int fd, short type, void *arg) 571{ 572 struct snmp_message *msg = arg; 573 ssize_t len; 574 575 if (type == EV_TIMEOUT || msg->sm_datalen >= sizeof(msg->sm_data)) 576 goto fail; 577 578 len = read(fd, msg->sm_data + msg->sm_datalen, 579 sizeof(msg->sm_data) - msg->sm_datalen); 580 if (len <= 0) { 581 if (errno != EAGAIN && errno != EINTR) 582 goto fail; 583 snmpe_prepare_read(msg, fd); 584 return; 585 } 586 587 msg->sm_datalen += (size_t)len; 588 snmpe_tryparse(fd, msg); 589 return; 590 591 fail: 592 snmp_msgfree(msg); 593 close(fd); 594} 595 596void 597snmpe_writecb(int fd, short type, void *arg) 598{ 599 struct snmp_stats *stats = &snmpd_env->sc_stats; 600 struct snmp_message *msg = arg; 601 struct snmp_message *nmsg; 602 ssize_t len; 603 size_t reqlen; 604 struct ber *ber = &msg->sm_ber; 605 606 if (type == EV_TIMEOUT) 607 goto fail; 608 609 len = ber->br_wend - ber->br_wptr; 610 611 log_debug("%s: write fd %d len %zd", __func__, fd, len); 612 613 len = write(fd, ber->br_wptr, len); 614 if (len == -1) { 615 if (errno == EAGAIN || errno == EINTR) 616 return; 617 else 618 goto fail; 619 } 620 621 ber->br_wptr += len; 622 623 if (ber->br_wptr < ber->br_wend) { 624 event_del(&msg->sm_sockev); 625 event_set(&msg->sm_sockev, msg->sm_sock, EV_WRITE, 626 snmpe_writecb, msg); 627 event_add(&msg->sm_sockev, &snmpe_tcp_timeout); 628 return; 629 } 630 631 stats->snmp_outpkts++; 632 633 if ((nmsg = calloc(1, sizeof(*nmsg))) == NULL) 634 goto fail; 635 memcpy(&(nmsg->sm_ss), &(msg->sm_ss), msg->sm_slen); 636 nmsg->sm_slen = msg->sm_slen; 637 nmsg->sm_aflags = msg->sm_aflags; 638 nmsg->sm_port = msg->sm_port; 639 640 /* 641 * Reuse the connection. 642 * In case we already read data of the next message, copy it over. 643 */ 644 reqlen = ober_calc_len(msg->sm_req); 645 if (msg->sm_datalen > reqlen) { 646 memcpy(nmsg->sm_data, msg->sm_data + reqlen, 647 msg->sm_datalen - reqlen); 648 nmsg->sm_datalen = msg->sm_datalen - reqlen; 649 snmp_msgfree(msg); 650 snmpe_tryparse(fd, nmsg); 651 } else { 652 snmp_msgfree(msg); 653 snmpe_prepare_read(nmsg, fd); 654 } 655 return; 656 657 fail: 658 close(fd); 659 snmp_msgfree(msg); 660} 661 662void 663snmpe_recvmsg(int fd, short sig, void *arg) 664{ 665 struct address *h = arg; 666 struct snmp_stats *stats = &snmpd_env->sc_stats; 667 ssize_t len; 668 struct snmp_message *msg; 669 670 if ((msg = calloc(1, sizeof(*msg))) == NULL) 671 return; 672 673 msg->sm_aflags = h->flags; 674 msg->sm_sock = fd; 675 msg->sm_slen = sizeof(msg->sm_ss); 676 msg->sm_port = h->port; 677 if ((len = recvfromto(fd, msg->sm_data, sizeof(msg->sm_data), 0, 678 (struct sockaddr *)&msg->sm_ss, &msg->sm_slen, 679 (struct sockaddr *)&msg->sm_local_ss, &msg->sm_local_slen)) < 1) { 680 free(msg); 681 return; 682 } 683 684 stats->snmp_inpkts++; 685 msg->sm_datalen = (size_t)len; 686 687 bzero(&msg->sm_ber, sizeof(msg->sm_ber)); 688 ober_set_application(&msg->sm_ber, smi_application); 689 ober_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen); 690 691 msg->sm_req = ober_read_elements(&msg->sm_ber, NULL); 692 if (msg->sm_req == NULL) { 693 stats->snmp_inasnparseerrs++; 694 snmp_msgfree(msg); 695 return; 696 } 697 698#ifdef DEBUG 699 fprintf(stderr, "recv msg:\n"); 700 smi_debug_elements(msg->sm_req); 701#endif 702 703 if (snmpe_parse(msg) == -1) { 704 if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) { 705 usm_make_report(msg); 706 return; 707 } else { 708 snmp_msgfree(msg); 709 return; 710 } 711 } 712 713 snmpe_dispatchmsg(msg); 714} 715 716void 717snmpe_dispatchmsg(struct snmp_message *msg) 718{ 719 if (msg->sm_pdutype == SNMP_C_TRAP || 720 msg->sm_pdutype == SNMP_C_TRAPV2) { 721 snmp_msgfree(msg); 722 return; 723 } 724 /* dispatched to subagent */ 725 /* XXX Do proper error handling */ 726 (void) snmpe_parsevarbinds(msg); 727 728 return; 729 /* 730 * Leave code here for now so it's easier to switch back in case of 731 * issues. 732 */ 733 /* respond directly */ 734 msg->sm_pdutype = SNMP_C_RESPONSE; 735 snmpe_response(msg); 736} 737 738void 739snmpe_send(struct snmp_message *msg, enum snmp_pdutype type, int32_t requestid, 740 int32_t error, uint32_t index, struct ber_element *varbindlist) 741{ 742 msg->sm_request = requestid; 743 msg->sm_pdutype = type; 744 msg->sm_error = error; 745 msg->sm_errorindex = index; 746 msg->sm_varbindresp = varbindlist; 747 748 snmpe_response(msg); 749} 750 751void 752snmpe_response(struct snmp_message *msg) 753{ 754 struct snmp_stats *stats = &snmpd_env->sc_stats; 755 u_int8_t *ptr = NULL; 756 ssize_t len; 757 758 if (msg->sm_varbindresp == NULL && msg->sm_pduend != NULL) 759 msg->sm_varbindresp = ober_unlink_elements(msg->sm_pduend); 760 761 switch (msg->sm_error) { 762 case SNMP_ERROR_NONE: 763 break; 764 case SNMP_ERROR_TOOBIG: 765 stats->snmp_intoobigs++; 766 break; 767 case SNMP_ERROR_NOSUCHNAME: 768 stats->snmp_innosuchnames++; 769 break; 770 case SNMP_ERROR_BADVALUE: 771 stats->snmp_inbadvalues++; 772 break; 773 case SNMP_ERROR_READONLY: 774 stats->snmp_inreadonlys++; 775 break; 776 case SNMP_ERROR_GENERR: 777 default: 778 stats->snmp_ingenerrs++; 779 break; 780 } 781 782 /* Create new SNMP packet */ 783 if (snmpe_encode(msg) < 0) 784 goto done; 785 786 len = ober_write_elements(&msg->sm_ber, msg->sm_resp); 787 if (ober_get_writebuf(&msg->sm_ber, (void *)&ptr) == -1) 788 goto done; 789 790 usm_finalize_digest(msg, ptr, len); 791 if (msg->sm_sock_tcp) { 792 msg->sm_ber.br_wptr = msg->sm_ber.br_wbuf; 793 event_del(&msg->sm_sockev); 794 event_set(&msg->sm_sockev, msg->sm_sock, EV_WRITE, 795 snmpe_writecb, msg); 796 event_add(&msg->sm_sockev, &snmpe_tcp_timeout); 797 return; 798 } else { 799 len = sendtofrom(msg->sm_sock, ptr, len, 0, 800 (struct sockaddr *)&msg->sm_ss, msg->sm_slen, 801 (struct sockaddr *)&msg->sm_local_ss, msg->sm_local_slen); 802 if (len != -1) 803 stats->snmp_outpkts++; 804 } 805 806 done: 807 snmp_msgfree(msg); 808} 809 810void 811snmp_msgfree(struct snmp_message *msg) 812{ 813 if (msg->sm_transactionid != 0) 814 RB_REMOVE(snmp_messages, &snmp_messages, msg); 815 event_del(&msg->sm_sockev); 816 ober_free(&msg->sm_ber); 817 if (msg->sm_req != NULL) 818 ober_free_elements(msg->sm_req); 819 if (msg->sm_resp != NULL) 820 ober_free_elements(msg->sm_resp); 821 free(msg); 822} 823 824int 825snmpe_encode(struct snmp_message *msg) 826{ 827 struct ber_element *ehdr; 828 struct ber_element *pdu, *epdu; 829 830 msg->sm_resp = ober_add_sequence(NULL); 831 if ((ehdr = ober_add_integer(msg->sm_resp, msg->sm_version)) == NULL) 832 return -1; 833 if (msg->sm_version == SNMP_V3) { 834 char f = MSG_SECLEVEL(msg); 835 836 if ((ehdr = ober_printf_elements(ehdr, "{iixi}", msg->sm_msgid, 837 msg->sm_max_msg_size, &f, sizeof(f), 838 msg->sm_secmodel)) == NULL) 839 return -1; 840 841 /* XXX currently only USM supported */ 842 if ((ehdr = usm_encode(msg, ehdr)) == NULL) 843 return -1; 844 } else { 845 if ((ehdr = ober_add_string(ehdr, msg->sm_community)) == NULL) 846 return -1; 847 } 848 849 pdu = epdu = ober_add_sequence(NULL); 850 if (msg->sm_version == SNMP_V3) { 851 if ((epdu = ober_printf_elements(epdu, "xs{", 852 snmpd_env->sc_engineid, snmpd_env->sc_engineid_len, 853 msg->sm_ctxname)) == NULL) { 854 ober_free_elements(pdu); 855 return -1; 856 } 857 } 858 859 if (!ober_printf_elements(epdu, "tiii{e}", BER_CLASS_CONTEXT, 860 msg->sm_pdutype, msg->sm_request, 861 msg->sm_error, msg->sm_errorindex, 862 msg->sm_varbindresp)) { 863 ober_free_elements(pdu); 864 return -1; 865 } 866 867 if (MSG_HAS_PRIV(msg)) 868 pdu = usm_encrypt(msg, pdu); 869 ober_link_elements(ehdr, pdu); 870 871#ifdef DEBUG 872 fprintf(stderr, "resp msg:\n"); 873 smi_debug_elements(msg->sm_resp); 874#endif 875 return 0; 876} 877 878int 879snmp_messagecmp(struct snmp_message *m1, struct snmp_message *m2) 880{ 881 return (m1->sm_transactionid < m2->sm_transactionid ? -1 : 882 m1->sm_transactionid > m2->sm_transactionid); 883} 884 885RB_GENERATE(snmp_messages, snmp_message, sm_entry, snmp_messagecmp) 886