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