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