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 /*
 485  * Destroy DCCP kernel statistics.
 486  */
 487 void
 488 dccp_kstat2_fini(netstackid_t stackid, kstat_t *ksp)
 489 {
 490         if (ksp != NULL) {
 491                 ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
 492                 kstat_delete_netstack(ksp, stackid);
 493         }
 494 }
 495 
 496 /*
 497  * Update routine for .
 498  */
 499 static int
 500 dccp_kstat2_update(kstat_t *kp, int rw)
 501 {
 502         dccp_stack_t    *dccps;
 503         dccp_stat_t     *stats;
 504         netstack_t      *ns;
 505         netstackid_t    stackid;
 506         int             i;
 507         int             cnt;
 508 
 509         if (rw == KSTAT_WRITE) {
 510                 return (EACCES);
 511         }
 512 
 513         stackid = (netstackid_t)(uintptr_t)kp->ks_private;
 514         ns = netstack_find_by_stackid(stackid);
 515         if (ns == NULL) {
 516                 return (-1);
 517         }
 518 
 519         dccps = ns->netstack_dccp;
 520         if (dccps == NULL) {
 521                 netstack_rele(ns);
 522                 return (-1);
 523         }
 524 
 525         stats = (dccp_stat_t *)kp->ks_data;
 526         dccp_clr_stats(stats);
 527 
 528         /* Sum up all stats */
 529         cnt = dccps->dccps_sc_cnt;
 530         for (i = 0; i < cnt; i++) {
 531                 dccp_add_stats(&dccps->dccps_sc[i]->dccp_sc_stats, stats);
 532         }
 533 
 534         netstack_rele(ns);
 535 
 536         return (0);
 537 }
 538 
 539 /*
 540  * Add stats from one to another.
 541  */
 542 static void
 543 dccp_add_mib(mib2_dccp_t *from, mib2_dccp_t *to)
 544 {
 545         to->dccpActiveOpens += from->dccpActiveOpens;
 546         to->dccpPassiveOpens += from->dccpPassiveOpens;
 547         to->dccpInSegs += from->dccpInSegs;
 548         to->dccpOutSegs += from->dccpOutSegs;
 549 }
 550 
 551 /*
 552  * Sum up all MIB-II stats for a dccp_stack_t from all per CPU stats.
 553  */
 554 static void
 555 dccp_sum_mib(dccp_stack_t *dccps, mib2_dccp_t *dccp_mib)
 556 {
 557         int     i;
 558         int     cnt;
 559 
 560         cnt = dccps->dccps_sc_cnt;
 561         for (i = 0; i < cnt; i++) {
 562                 dccp_add_mib(&dccps->dccps_sc[i]->dccp_sc_mib, dccp_mib);
 563         }
 564 }
 565 
 566 /*
 567  * Set all dccp_stat_t counters to zero.
 568  */
 569 static void
 570 dccp_clr_stats(dccp_stat_t *stats)
 571 {
 572         stats->dccp_sock_fallback.value.ui64 = 0;
 573 }
 574 
 575 /*
 576  * Add counters from the per CPU stats.
 577  */
 578 static void
 579 dccp_add_stats(dccp_stat_counter_t *from, dccp_stat_t *to)
 580 {
 581         to->dccp_sock_fallback.value.ui64 +=
 582             from->dccp_sock_fallback;
 583 }