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 }