1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright 2012 David Hoeppner. All rights reserved. 29 */ 30 31 /* 32 * Functions related to MIB-II and kstat. 33 */ 34 35 #include <sys/types.h> 36 #include <sys/tihdr.h> 37 #include <sys/policy.h> 38 #include <sys/tsol/tnet.h> 39 40 #include <inet/common.h> 41 #include <inet/dccp_impl.h> 42 #include <inet/ip.h> 43 #include <inet/kstatcom.h> 44 #include <inet/snmpcom.h> 45 46 #include <sys/cmn_err.h> 47 48 static int dccp_snmp_state(dccp_t *); 49 static int dccp_kstat_update(kstat_t *, int); 50 static int dccp_kstat2_update(kstat_t *, int); 51 static void dccp_add_mib(mib2_dccp_t *, mib2_dccp_t *); 52 static void dccp_sum_mib(dccp_stack_t *, mib2_dccp_t *); 53 static void dccp_clr_stats(dccp_stat_t *); 54 static void dccp_add_stats(dccp_stat_counter_t *, dccp_stat_t *); 55 56 /* 57 * Translate DCCP state to MIB2 state. 58 */ 59 static int 60 dccp_snmp_state(dccp_t *dccp) 61 { 62 if (dccp == NULL) { 63 return (0); 64 } 65 66 switch(dccp->dccp_state) { 67 case DCCPS_CLOSED: 68 return (MIB2_DCCP_closed); 69 default: 70 return (0); 71 } 72 } 73 74 /* 75 * Get the MIB-II stats. 76 */ 77 mblk_t * 78 dccp_snmp_get(queue_t *q, mblk_t *mpctl, boolean_t legacy_req) 79 { 80 conn_t *connp = Q_TO_CONN(q); 81 connf_t *connfp; 82 ip_stack_t *ipst; 83 dccp_stack_t *dccps; 84 struct opthdr *optp; 85 mblk_t *mp2ctl; 86 mblk_t *mpdata; 87 mblk_t *mp_conn_ctl = NULL; 88 mblk_t *mp_conn_tail; 89 mblk_t *mp_attr_ctl = NULL; 90 mblk_t *mp_attr_tail; 91 mblk_t *mp6_conn_ctl = NULL; 92 mblk_t *mp6_conn_tail; 93 mblk_t *mp6_attr_ctl = NULL; 94 mblk_t *mp6_attr_tail; 95 size_t dccp_mib_size; 96 size_t dce_size; 97 size_t dce6_size; 98 boolean_t ispriv; 99 zoneid_t zoneid; 100 int v4_conn_idx; 101 int v6_conn_idx; 102 int i; 103 mib2_dccp_t dccp_mib; 104 mib2_dccpConnEntry_t dce; 105 mib2_dccp6ConnEntry_t dce6; 106 mib2_transportMLPEntry_t mlp; 107 108 /* 109 * Make a copy of the original message. 110 */ 111 mp2ctl = copymsg(mpctl); 112 113 cmn_err(CE_NOTE, "dccp_stats.c: dccp_snmp_get"); 114 115 if (mpctl == NULL || 116 (mpdata = mpctl->b_cont) == NULL || 117 (mp_conn_ctl = copymsg(mpctl)) == NULL || 118 (mp_attr_ctl = copymsg(mpctl)) == NULL || 119 (mp6_conn_ctl = copymsg(mpctl)) == NULL || 120 (mp6_attr_ctl = copymsg(mpctl)) == NULL) { 121 freemsg(mp_conn_ctl); 122 freemsg(mp_attr_ctl); 123 freemsg(mp6_conn_ctl); 124 freemsg(mp6_attr_ctl); 125 freemsg(mpctl); 126 freemsg(mp2ctl); 127 return (NULL); 128 } 129 130 ipst = connp->conn_netstack->netstack_ip; 131 dccps = connp->conn_netstack->netstack_dccp; 132 133 if (legacy_req) { 134 dccp_mib_size = LEGACY_MIB_SIZE(&dccp_mib, mib2_dccp_t); 135 dce_size = LEGACY_MIB_SIZE(&dce, mib2_dccpConnEntry_t); 136 dce6_size = LEGACY_MIB_SIZE(&dce6, mib2_dccp6ConnEntry_t); 137 } else { 138 dccp_mib_size = sizeof (mib2_dccp_t); 139 dce_size = sizeof (mib2_dccpConnEntry_t); 140 dce6_size = sizeof (mib2_dccp6ConnEntry_t); 141 } 142 143 bzero(&dccp_mib, sizeof (dccp_mib)); 144 145 ispriv = secpolicy_ip_config((Q_TO_CONN(q))->conn_cred, B_TRUE) == 0; 146 zoneid = Q_TO_CONN(q)->conn_zoneid; 147 148 v4_conn_idx = v6_conn_idx = 0; 149 mp_conn_tail = mp_attr_tail = mp6_conn_tail = mp6_attr_tail = NULL; 150 151 for (i = 0; i < CONN_G_HASH_SIZE; i++) { 152 ipst = dccps->dccps_netstack->netstack_ip; 153 154 connfp = &ipst->ips_ipcl_globalhash_fanout[i]; 155 connp = NULL; 156 157 while ((connp = ipcl_get_next_conn(connfp, connp, 158 IPCL_DCCPCONN)) != NULL) { 159 dccp_t *dccp; 160 boolean_t needattr; 161 162 if (connp->conn_zoneid != zoneid) { 163 continue; /* Not in this zone */ 164 } 165 166 dccp = connp->conn_dccp; 167 DCCPS_UPDATE_MIB(dccps, dccpHCInSegs, dccp->dccp_ibsegs); 168 dccp->dccp_ibsegs = 0; 169 DCCPS_UPDATE_MIB(dccps, dccpHCOutSegs, dccp->dccp_obsegs); 170 dccp->dccp_obsegs = 0; 171 172 dce.dccpConnState = dccp_snmp_state(dccp); 173 174 needattr = B_FALSE; 175 bzero(&mlp, sizeof (mlp)); 176 if (connp->conn_mlp_type != mlptSingle) { 177 if (connp->conn_mlp_type == mlptShared || 178 connp->conn_mlp_type == mlptBoth) { 179 mlp.tme_flags |= MIB2_TMEF_SHARED; 180 } 181 182 if (connp->conn_mlp_type == mlptPrivate || 183 connp->conn_mlp_type == mlptBoth) { 184 mlp.tme_flags |= MIB2_TMEF_PRIVATE; 185 } 186 187 needattr = B_TRUE; 188 } 189 190 if (connp->conn_anon_mlp) { 191 mlp.tme_flags |= MIB2_TMEF_ANONMLP; 192 needattr = B_TRUE; 193 } 194 195 switch (connp->conn_mac_mode) { 196 case CONN_MAC_DEFAULT: 197 break; 198 case CONN_MAC_AWARE: 199 mlp.tme_flags |= MIB2_TMEF_MACEXEMPT; 200 needattr = B_TRUE; 201 break; 202 case CONN_MAC_IMPLICIT: 203 mlp.tme_flags |= MIB2_TMEF_MACIMPLICIT; 204 needattr = B_TRUE; 205 break; 206 } 207 208 if (connp->conn_ixa->ixa_tsl != NULL) { 209 ts_label_t *tsl; 210 211 tsl = connp->conn_ixa->ixa_tsl; 212 mlp.tme_flags |= MIB2_TMEF_IS_LABELED; 213 mlp.tme_doi = label2doi(tsl); 214 mlp.tme_label = *label2bslabel(tsl); 215 needattr = B_TRUE; 216 } 217 218 /* Create a message to report on IPv6 entries */ 219 if (connp->conn_ipversion == IPV6_VERSION) { 220 dce6.dccp6ConnLocalAddress = 221 connp->conn_laddr_v6; 222 dce6.dccp6ConnRemAddress = 223 connp->conn_faddr_v6; 224 dce6.dccp6ConnLocalPort = 225 ntohs(connp->conn_lport); 226 dce6.dccp6ConnRemPort = 227 ntohs(connp->conn_fport); 228 229 if (connp->conn_ixa->ixa_flags & 230 IXAF_SCOPEID_SET) { 231 dce6.dccp6ConnIfIndex = 232 connp->conn_ixa->ixa_scopeid; 233 } else { 234 dce6.dccp6ConnIfIndex = 235 connp->conn_bound_if; 236 } 237 238 /* XXX */ 239 240 dce6.dccp6ConnEntryInfo.ce_state = 241 dccp->dccp_state; 242 243 dce6.dccp6ConnCreationProcess = 244 (connp->conn_cpid < 0) ? 245 MIB2_UNKNOWN_PROCESS : connp->conn_cpid; 246 dce6.dccp6ConnCreationTime = 247 connp->conn_open_time; 248 249 (void) snmp_append_data2(mp6_conn_ctl->b_cont, 250 &mp6_conn_tail, (char *)&dce6, dce6_size); 251 252 mlp.tme_connidx = v6_conn_idx++; 253 if (needattr) { 254 (void) snmp_append_data2( 255 mp6_attr_ctl->b_cont, 256 &mp6_attr_tail, (char *)&mlp, 257 sizeof (mlp)); 258 } 259 } 260 261 if (connp->conn_ipversion == IPV4_VERSION || 262 (dccp->dccp_state <= DCCPS_LISTEN && 263 !connp->conn_ipv6_v6only && 264 IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6))) { 265 266 if (connp->conn_ipversion == IPV6_VERSION) { 267 dce.dccpConnRemAddress = INADDR_ANY; 268 dce.dccpConnLocalAddress = INADDR_ANY; 269 } else { 270 dce.dccpConnRemAddress = 271 connp->conn_faddr_v4; 272 dce.dccpConnLocalAddress = 273 connp->conn_laddr_v4; 274 } 275 276 dce.dccpConnLocalPort = 277 ntohs(connp->conn_lport); 278 dce.dccpConnRemPort = 279 ntohs(connp->conn_fport); 280 281 /* XXX */ 282 283 dce.dccpConnEntryInfo.ce_state = 284 dccp->dccp_state; 285 286 dce.dccpConnCreationProcess = 287 (connp->conn_cpid < 0) ? 288 MIB2_UNKNOWN_PROCESS : connp->conn_cpid; 289 dce.dccpConnCreationTime = 290 connp->conn_open_time; 291 292 (void) snmp_append_data2(mp_conn_ctl->b_cont, 293 &mp_conn_tail, (char *)&dce, dce_size); 294 295 mlp.tme_connidx = v4_conn_idx++; 296 if (needattr) { 297 (void) snmp_append_data2( 298 mp_attr_ctl->b_cont, 299 &mp_attr_tail, (char *)&mlp, 300 sizeof (mlp)); 301 } 302 } 303 } 304 } 305 306 /* Sum up per CPU stats */ 307 dccp_sum_mib(dccps, &dccp_mib); 308 309 /* Fixed length structure for IPv4 and IPv6 counters */ 310 SET_MIB(dccp_mib.dccpConnTableSize, dce_size); 311 SET_MIB(dccp_mib.dccp6ConnTableSize, dce6_size); 312 313 /* Synchronize 32- and 64-bit counters */ 314 SYNC32_MIB(&dccp_mib, dccpInSegs, dccpHCInSegs); 315 SYNC32_MIB(&dccp_mib, dccpOutSegs, dccpHCOutSegs); 316 317 optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)]; 318 optp->level = MIB2_DCCP; 319 optp->name = 0; 320 (void) snmp_append_data(mpdata, (char *)&dccp_mib, dccp_mib_size); 321 optp->len = msgdsize(mpdata); 322 qreply(q, mpctl); 323 324 optp = (struct opthdr *)&mp_conn_ctl->b_rptr[ 325 sizeof (struct T_optmgmt_ack)]; 326 optp->level = MIB2_DCCP; 327 optp->name = MIB2_DCCP_CONN; 328 optp->len = msgdsize(mp_conn_ctl->b_cont); 329 qreply(q, mp_conn_ctl); 330 331 optp = (struct opthdr *)&mp_attr_ctl->b_rptr[ 332 sizeof (struct T_optmgmt_ack)]; 333 optp->level = MIB2_DCCP; 334 optp->name = EXPER_XPORT_MLP; 335 optp->len = msgdsize(mp_attr_ctl->b_cont); 336 if (optp->len == 0) { 337 freemsg(mp_attr_ctl); 338 } else { 339 qreply(q, mp_attr_ctl); 340 } 341 342 optp = (struct opthdr *)&mp6_conn_ctl->b_rptr[ 343 sizeof (struct T_optmgmt_ack)]; 344 optp->level = MIB2_DCCP6; 345 optp->name = MIB2_DCCP6_CONN; 346 optp->len = msgdsize(mp6_conn_ctl->b_cont); 347 qreply(q, mp6_conn_ctl); 348 349 optp = (struct opthdr *)&mp6_attr_ctl->b_rptr[ 350 sizeof (struct T_optmgmt_ack)]; 351 optp->level = MIB2_DCCP6; 352 optp->name = EXPER_XPORT_MLP; 353 optp->len = msgdsize(mp6_attr_ctl->b_cont); 354 if (optp->len == 0) { 355 freemsg(mp6_attr_ctl); 356 } else { 357 qreply(q, mp6_attr_ctl); 358 } 359 360 return (mp2ctl); 361 } 362 363 /* 364 * DCCP kernel statistics. 365 */ 366 void * 367 dccp_kstat_init(netstackid_t stackid) 368 { 369 kstat_t *ksp; 370 371 dccp_named_kstat_t template = { 372 { "activeOpens", KSTAT_DATA_UINT32, 0 }, 373 { "passiveOpens", KSTAT_DATA_UINT32, 0 }, 374 { "inSegs", KSTAT_DATA_UINT64, 0 }, 375 { "outSegs", KSTAT_DATA_UINT64, 0 }, 376 }; 377 378 ksp = kstat_create_netstack(DCCP_MOD_NAME, 0, DCCP_MOD_NAME, "mib2", 379 KSTAT_TYPE_NAMED, NUM_OF_FIELDS(dccp_named_kstat_t), 0, stackid); 380 if (ksp == NULL) { 381 return (NULL); 382 } 383 384 bcopy(&template, ksp->ks_data, sizeof (template)); 385 ksp->ks_update = dccp_kstat_update; 386 ksp->ks_private = (void *)(uintptr_t)stackid; 387 388 kstat_install(ksp); 389 390 return (ksp); 391 } 392 393 /* 394 * Destroy DCCP kernel statistics. 395 */ 396 void 397 dccp_kstat_fini(netstackid_t stackid, kstat_t *ksp) 398 { 399 400 if (ksp != NULL) { 401 ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private); 402 kstat_delete_netstack(ksp, stackid); 403 } 404 } 405 406 /* 407 * Update DCCP kernel statistics. 408 */ 409 static int 410 dccp_kstat_update(kstat_t *kp, int rw) 411 { 412 conn_t *connp; 413 connf_t *connfp; 414 dccp_named_kstat_t *dccpkp; 415 dccp_t *dccp; 416 dccp_stack_t *dccps; 417 ip_stack_t *ipst; 418 netstack_t *ns; 419 netstackid_t stackid; 420 mib2_dccp_t dccp_mib; 421 422 if (rw == KSTAT_WRITE) { 423 return (EACCES); 424 } 425 426 stackid = (netstackid_t)(uintptr_t)kp->ks_private; 427 ns = netstack_find_by_stackid(stackid); 428 if (ns == NULL) { 429 return (-1); 430 } 431 432 dccps = ns->netstack_dccp; 433 if (dccps == NULL) { 434 netstack_rele(ns); 435 return (-1); 436 } 437 438 dccpkp = (dccp_named_kstat_t *)kp->ks_data; 439 ipst = ns->netstack_ip; 440 441 bzero(&dccp_mib, sizeof (dccp_mib)); 442 dccp_sum_mib(dccps, &dccp_mib); 443 444 /* Fixed length structure for IPv4 and IPv6 counters */ 445 SET_MIB(dccp_mib.dccpConnTableSize, sizeof (mib2_dccpConnEntry_t)); 446 SET_MIB(dccp_mib.dccp6ConnTableSize, sizeof (mib2_dccp6ConnEntry_t)); 447 448 dccpkp->activeOpens.value.ui32 = dccp_mib.dccpActiveOpens; 449 dccpkp->passiveOpens.value.ui32 = dccp_mib.dccpPassiveOpens; 450 dccpkp->inSegs.value.ui64 = dccp_mib.dccpHCInSegs; 451 dccpkp->outSegs.value.ui64 = dccp_mib.dccpHCOutSegs; 452 453 return (0); 454 } 455 456 /* 457 * 458 */ 459 void * 460 dccp_kstat2_init(netstackid_t stackid) 461 { 462 kstat_t *ksp; 463 464 dccp_stat_t template = { 465 { "dccp_sock_fallback", KSTAT_DATA_UINT64, 0 }, 466 }; 467 468 ksp = kstat_create_netstack(DCCP_MOD_NAME, 0, "dccpstat", "net", 469 KSTAT_TYPE_NAMED, sizeof (template) / sizeof (kstat_named_t), 0, 470 stackid); 471 if (ksp == NULL) { 472 return (NULL); 473 } 474 475 bcopy(&template, ksp->ks_data, sizeof (template)); 476 ksp->ks_private = (void *)(uintptr_t)stackid; 477 ksp->ks_update = dccp_kstat2_update; 478 479 kstat_install(ksp); 480 481 return (ksp); 482 } 483 484 void 485 dccp_kstat2_fini(netstackid_t stackid, kstat_t *ksp) 486 { 487 if (ksp != NULL) { 488 ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private); 489 kstat_delete_netstack(ksp, stackid); 490 } 491 } 492 493 /* 494 * Update routine for . 495 */ 496 static int 497 dccp_kstat2_update(kstat_t *kp, int rw) 498 { 499 dccp_stack_t *dccps; 500 dccp_stat_t *stats; 501 netstack_t *ns; 502 netstackid_t stackid; 503 int i; 504 int cnt; 505 506 if (rw == KSTAT_WRITE) { 507 return (EACCES); 508 } 509 510 stackid = (netstackid_t)(uintptr_t)kp->ks_private; 511 ns = netstack_find_by_stackid(stackid); 512 if (ns == NULL) { 513 return (-1); 514 } 515 516 dccps = ns->netstack_dccp; 517 if (dccps == NULL) { 518 netstack_rele(ns); 519 return (-1); 520 } 521 522 stats = (dccp_stat_t *)kp->ks_data; 523 dccp_clr_stats(stats); 524 525 /* Sum up all stats */ 526 cnt = dccps->dccps_sc_cnt; 527 for (i = 0; i < cnt; i++) { 528 dccp_add_stats(&dccps->dccps_sc[i]->dccp_sc_stats, stats); 529 } 530 531 netstack_rele(ns); 532 533 return (0); 534 } 535 536 /* 537 * Add stats from one to another. 538 */ 539 static void 540 dccp_add_mib(mib2_dccp_t *from, mib2_dccp_t *to) 541 { 542 to->dccpActiveOpens += from->dccpActiveOpens; 543 to->dccpPassiveOpens += from->dccpPassiveOpens; 544 to->dccpInSegs += from->dccpInSegs; 545 to->dccpOutSegs += from->dccpOutSegs; 546 } 547 548 /* 549 * Sum up all MIB-II stats for a dccp_stack_t from all per CPU stats. 550 */ 551 static void 552 dccp_sum_mib(dccp_stack_t *dccps, mib2_dccp_t *dccp_mib) 553 { 554 int i; 555 int cnt; 556 557 cnt = dccps->dccps_sc_cnt; 558 for (i = 0; i < cnt; i++) { 559 dccp_add_mib(&dccps->dccps_sc[i]->dccp_sc_mib, dccp_mib); 560 } 561 } 562 563 /* 564 * Set all dccp_stat_t counters to zero. 565 */ 566 static void 567 dccp_clr_stats(dccp_stat_t *stats) 568 { 569 stats->dccp_sock_fallback.value.ui64 = 0; 570 } 571 572 /* 573 * Add counters from the per CPU stats. 574 */ 575 static void 576 dccp_add_stats(dccp_stat_counter_t *from, dccp_stat_t *to) 577 { 578 to->dccp_sock_fallback.value.ui64 += 579 from->dccp_sock_fallback; 580 }