xref: /illumos-gate/usr/src/uts/common/inet/udp/udp_stats.c (revision a386cc11a86ecb60f5a48078d22c1500e2ad003e)
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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/tihdr.h>
28 #include <sys/policy.h>
29 #include <sys/tsol/tnet.h>
30 
31 #include <inet/common.h>
32 #include <inet/kstatcom.h>
33 #include <inet/snmpcom.h>
34 #include <inet/mib2.h>
35 #include <inet/optcom.h>
36 #include <inet/snmpcom.h>
37 #include <inet/kstatcom.h>
38 #include <inet/udp_impl.h>
39 
40 static int	udp_kstat_update(kstat_t *, int);
41 static int	udp_kstat2_update(kstat_t *, int);
42 static void	udp_sum_mib(udp_stack_t *, mib2_udp_t *);
43 static void	udp_clr_stats(udp_stat_t *);
44 static void	udp_add_stats(udp_stat_counter_t *, udp_stat_t *);
45 static void	udp_add_mib(mib2_udp_t *, mib2_udp_t *);
46 /*
47  * return SNMP stuff in buffer in mpdata. We don't hold any lock and report
48  * information that can be changing beneath us.
49  */
50 mblk_t *
51 udp_snmp_get(queue_t *q, mblk_t *mpctl, boolean_t legacy_req)
52 {
53 	mblk_t			*mpdata;
54 	mblk_t			*mp_conn_ctl;
55 	mblk_t			*mp_attr_ctl;
56 	mblk_t			*mp6_conn_ctl;
57 	mblk_t			*mp6_attr_ctl;
58 	mblk_t			*mp_conn_tail;
59 	mblk_t			*mp_attr_tail;
60 	mblk_t			*mp6_conn_tail;
61 	mblk_t			*mp6_attr_tail;
62 	struct opthdr		*optp;
63 	mib2_udpEntry_t		ude;
64 	mib2_udp6Entry_t	ude6;
65 	mib2_transportMLPEntry_t mlp;
66 	int			state;
67 	zoneid_t		zoneid;
68 	int			i;
69 	connf_t			*connfp;
70 	conn_t			*connp = Q_TO_CONN(q);
71 	int			v4_conn_idx;
72 	int			v6_conn_idx;
73 	boolean_t		needattr;
74 	udp_t			*udp;
75 	ip_stack_t		*ipst = connp->conn_netstack->netstack_ip;
76 	udp_stack_t		*us = connp->conn_netstack->netstack_udp;
77 	mblk_t			*mp2ctl;
78 	mib2_udp_t		udp_mib;
79 	size_t			udp_mib_size, ude_size, ude6_size;
80 
81 
82 	/*
83 	 * make a copy of the original message
84 	 */
85 	mp2ctl = copymsg(mpctl);
86 
87 	mp_conn_ctl = mp_attr_ctl = mp6_conn_ctl = NULL;
88 	if (mpctl == NULL ||
89 	    (mpdata = mpctl->b_cont) == NULL ||
90 	    (mp_conn_ctl = copymsg(mpctl)) == NULL ||
91 	    (mp_attr_ctl = copymsg(mpctl)) == NULL ||
92 	    (mp6_conn_ctl = copymsg(mpctl)) == NULL ||
93 	    (mp6_attr_ctl = copymsg(mpctl)) == NULL) {
94 		freemsg(mp_conn_ctl);
95 		freemsg(mp_attr_ctl);
96 		freemsg(mp6_conn_ctl);
97 		freemsg(mpctl);
98 		freemsg(mp2ctl);
99 		return (0);
100 	}
101 
102 	zoneid = connp->conn_zoneid;
103 
104 	if (legacy_req) {
105 		udp_mib_size = LEGACY_MIB_SIZE(&udp_mib, mib2_udp_t);
106 		ude_size = LEGACY_MIB_SIZE(&ude, mib2_udpEntry_t);
107 		ude6_size = LEGACY_MIB_SIZE(&ude6, mib2_udp6Entry_t);
108 	} else {
109 		udp_mib_size = sizeof (mib2_udp_t);
110 		ude_size = sizeof (mib2_udpEntry_t);
111 		ude6_size = sizeof (mib2_udp6Entry_t);
112 	}
113 
114 	bzero(&udp_mib, sizeof (udp_mib));
115 	/* fixed length structure for IPv4 and IPv6 counters */
116 	SET_MIB(udp_mib.udpEntrySize, ude_size);
117 	SET_MIB(udp_mib.udp6EntrySize, ude6_size);
118 
119 	udp_sum_mib(us, &udp_mib);
120 
121 	/*
122 	 * Synchronize 32- and 64-bit counters.  Note that udpInDatagrams and
123 	 * udpOutDatagrams are not updated anywhere in UDP.  The new 64 bits
124 	 * counters are used.  Hence the old counters' values in us_sc_mib
125 	 * are always 0.
126 	 */
127 	SYNC32_MIB(&udp_mib, udpInDatagrams, udpHCInDatagrams);
128 	SYNC32_MIB(&udp_mib, udpOutDatagrams, udpHCOutDatagrams);
129 
130 	optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
131 	optp->level = MIB2_UDP;
132 	optp->name = 0;
133 	(void) snmp_append_data(mpdata, (char *)&udp_mib, udp_mib_size);
134 	optp->len = msgdsize(mpdata);
135 	qreply(q, mpctl);
136 
137 	mp_conn_tail = mp_attr_tail = mp6_conn_tail = mp6_attr_tail = NULL;
138 	v4_conn_idx = v6_conn_idx = 0;
139 
140 	for (i = 0; i < CONN_G_HASH_SIZE; i++) {
141 		connfp = &ipst->ips_ipcl_globalhash_fanout[i];
142 		connp = NULL;
143 
144 		while ((connp = ipcl_get_next_conn(connfp, connp,
145 		    IPCL_UDPCONN))) {
146 			udp = connp->conn_udp;
147 			if (zoneid != connp->conn_zoneid)
148 				continue;
149 
150 			/*
151 			 * Note that the port numbers are sent in
152 			 * host byte order
153 			 */
154 
155 			if (udp->udp_state == TS_UNBND)
156 				state = MIB2_UDP_unbound;
157 			else if (udp->udp_state == TS_IDLE)
158 				state = MIB2_UDP_idle;
159 			else if (udp->udp_state == TS_DATA_XFER)
160 				state = MIB2_UDP_connected;
161 			else
162 				state = MIB2_UDP_unknown;
163 
164 			needattr = B_FALSE;
165 			bzero(&mlp, sizeof (mlp));
166 			if (connp->conn_mlp_type != mlptSingle) {
167 				if (connp->conn_mlp_type == mlptShared ||
168 				    connp->conn_mlp_type == mlptBoth)
169 					mlp.tme_flags |= MIB2_TMEF_SHARED;
170 				if (connp->conn_mlp_type == mlptPrivate ||
171 				    connp->conn_mlp_type == mlptBoth)
172 					mlp.tme_flags |= MIB2_TMEF_PRIVATE;
173 				needattr = B_TRUE;
174 			}
175 			if (connp->conn_anon_mlp) {
176 				mlp.tme_flags |= MIB2_TMEF_ANONMLP;
177 				needattr = B_TRUE;
178 			}
179 			switch (connp->conn_mac_mode) {
180 			case CONN_MAC_DEFAULT:
181 				break;
182 			case CONN_MAC_AWARE:
183 				mlp.tme_flags |= MIB2_TMEF_MACEXEMPT;
184 				needattr = B_TRUE;
185 				break;
186 			case CONN_MAC_IMPLICIT:
187 				mlp.tme_flags |= MIB2_TMEF_MACIMPLICIT;
188 				needattr = B_TRUE;
189 				break;
190 			}
191 			mutex_enter(&connp->conn_lock);
192 			if (udp->udp_state == TS_DATA_XFER &&
193 			    connp->conn_ixa->ixa_tsl != NULL) {
194 				ts_label_t *tsl;
195 
196 				tsl = connp->conn_ixa->ixa_tsl;
197 				mlp.tme_flags |= MIB2_TMEF_IS_LABELED;
198 				mlp.tme_doi = label2doi(tsl);
199 				mlp.tme_label = *label2bslabel(tsl);
200 				needattr = B_TRUE;
201 			}
202 			mutex_exit(&connp->conn_lock);
203 
204 			/*
205 			 * Create an IPv4 table entry for IPv4 entries and also
206 			 * any IPv6 entries which are bound to in6addr_any
207 			 * (i.e. anything a IPv4 peer could connect/send to).
208 			 */
209 			if (connp->conn_ipversion == IPV4_VERSION ||
210 			    (udp->udp_state <= TS_IDLE &&
211 			    IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6))) {
212 				ude.udpEntryInfo.ue_state = state;
213 				/*
214 				 * If in6addr_any this will set it to
215 				 * INADDR_ANY
216 				 */
217 				ude.udpLocalAddress = connp->conn_laddr_v4;
218 				ude.udpLocalPort = ntohs(connp->conn_lport);
219 				if (udp->udp_state == TS_DATA_XFER) {
220 					/*
221 					 * Can potentially get here for
222 					 * v6 socket if another process
223 					 * (say, ping) has just done a
224 					 * sendto(), changing the state
225 					 * from the TS_IDLE above to
226 					 * TS_DATA_XFER by the time we hit
227 					 * this part of the code.
228 					 */
229 					ude.udpEntryInfo.ue_RemoteAddress =
230 					    connp->conn_faddr_v4;
231 					ude.udpEntryInfo.ue_RemotePort =
232 					    ntohs(connp->conn_fport);
233 				} else {
234 					ude.udpEntryInfo.ue_RemoteAddress = 0;
235 					ude.udpEntryInfo.ue_RemotePort = 0;
236 				}
237 
238 				/*
239 				 * We make the assumption that all udp_t
240 				 * structs will be created within an address
241 				 * region no larger than 32-bits.
242 				 */
243 				ude.udpInstance = (uint32_t)(uintptr_t)udp;
244 				ude.udpCreationProcess =
245 				    (connp->conn_cpid < 0) ?
246 				    MIB2_UNKNOWN_PROCESS :
247 				    connp->conn_cpid;
248 				ude.udpCreationTime = connp->conn_open_time;
249 
250 				(void) snmp_append_data2(mp_conn_ctl->b_cont,
251 				    &mp_conn_tail, (char *)&ude, ude_size);
252 				mlp.tme_connidx = v4_conn_idx++;
253 				if (needattr)
254 					(void) snmp_append_data2(
255 					    mp_attr_ctl->b_cont, &mp_attr_tail,
256 					    (char *)&mlp, sizeof (mlp));
257 			}
258 			if (connp->conn_ipversion == IPV6_VERSION) {
259 				ude6.udp6EntryInfo.ue_state  = state;
260 				ude6.udp6LocalAddress = connp->conn_laddr_v6;
261 				ude6.udp6LocalPort = ntohs(connp->conn_lport);
262 				mutex_enter(&connp->conn_lock);
263 				if (connp->conn_ixa->ixa_flags &
264 				    IXAF_SCOPEID_SET) {
265 					ude6.udp6IfIndex =
266 					    connp->conn_ixa->ixa_scopeid;
267 				} else {
268 					ude6.udp6IfIndex = connp->conn_bound_if;
269 				}
270 				mutex_exit(&connp->conn_lock);
271 				if (udp->udp_state == TS_DATA_XFER) {
272 					ude6.udp6EntryInfo.ue_RemoteAddress =
273 					    connp->conn_faddr_v6;
274 					ude6.udp6EntryInfo.ue_RemotePort =
275 					    ntohs(connp->conn_fport);
276 				} else {
277 					ude6.udp6EntryInfo.ue_RemoteAddress =
278 					    sin6_null.sin6_addr;
279 					ude6.udp6EntryInfo.ue_RemotePort = 0;
280 				}
281 				/*
282 				 * We make the assumption that all udp_t
283 				 * structs will be created within an address
284 				 * region no larger than 32-bits.
285 				 */
286 				ude6.udp6Instance = (uint32_t)(uintptr_t)udp;
287 				ude6.udp6CreationProcess =
288 				    (connp->conn_cpid < 0) ?
289 				    MIB2_UNKNOWN_PROCESS :
290 				    connp->conn_cpid;
291 				ude6.udp6CreationTime = connp->conn_open_time;
292 
293 				(void) snmp_append_data2(mp6_conn_ctl->b_cont,
294 				    &mp6_conn_tail, (char *)&ude6, ude6_size);
295 				mlp.tme_connidx = v6_conn_idx++;
296 				if (needattr)
297 					(void) snmp_append_data2(
298 					    mp6_attr_ctl->b_cont,
299 					    &mp6_attr_tail, (char *)&mlp,
300 					    sizeof (mlp));
301 			}
302 		}
303 	}
304 
305 	/* IPv4 UDP endpoints */
306 	optp = (struct opthdr *)&mp_conn_ctl->b_rptr[
307 	    sizeof (struct T_optmgmt_ack)];
308 	optp->level = MIB2_UDP;
309 	optp->name = MIB2_UDP_ENTRY;
310 	optp->len = msgdsize(mp_conn_ctl->b_cont);
311 	qreply(q, mp_conn_ctl);
312 
313 	/* table of MLP attributes... */
314 	optp = (struct opthdr *)&mp_attr_ctl->b_rptr[
315 	    sizeof (struct T_optmgmt_ack)];
316 	optp->level = MIB2_UDP;
317 	optp->name = EXPER_XPORT_MLP;
318 	optp->len = msgdsize(mp_attr_ctl->b_cont);
319 	if (optp->len == 0)
320 		freemsg(mp_attr_ctl);
321 	else
322 		qreply(q, mp_attr_ctl);
323 
324 	/* IPv6 UDP endpoints */
325 	optp = (struct opthdr *)&mp6_conn_ctl->b_rptr[
326 	    sizeof (struct T_optmgmt_ack)];
327 	optp->level = MIB2_UDP6;
328 	optp->name = MIB2_UDP6_ENTRY;
329 	optp->len = msgdsize(mp6_conn_ctl->b_cont);
330 	qreply(q, mp6_conn_ctl);
331 
332 	/* table of MLP attributes... */
333 	optp = (struct opthdr *)&mp6_attr_ctl->b_rptr[
334 	    sizeof (struct T_optmgmt_ack)];
335 	optp->level = MIB2_UDP6;
336 	optp->name = EXPER_XPORT_MLP;
337 	optp->len = msgdsize(mp6_attr_ctl->b_cont);
338 	if (optp->len == 0)
339 		freemsg(mp6_attr_ctl);
340 	else
341 		qreply(q, mp6_attr_ctl);
342 
343 	return (mp2ctl);
344 }
345 
346 /*
347  * Return 0 if invalid set request, 1 otherwise, including non-udp requests.
348  * NOTE: Per MIB-II, UDP has no writable data.
349  * TODO:  If this ever actually tries to set anything, it needs to be
350  * to do the appropriate locking.
351  */
352 /* ARGSUSED */
353 int
354 udp_snmp_set(queue_t *q, t_scalar_t level, t_scalar_t name,
355     uchar_t *ptr, int len)
356 {
357 	switch (level) {
358 	case MIB2_UDP:
359 		return (0);
360 	default:
361 		return (1);
362 	}
363 }
364 
365 void
366 udp_kstat_fini(netstackid_t stackid, kstat_t *ksp)
367 {
368 	if (ksp != NULL) {
369 		ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
370 		kstat_delete_netstack(ksp, stackid);
371 	}
372 }
373 
374 /*
375  * To add stats from one mib2_udp_t to another.  Static fields are not added.
376  * The caller should set them up propertly.
377  */
378 static void
379 udp_add_mib(mib2_udp_t *from, mib2_udp_t *to)
380 {
381 	to->udpHCInDatagrams += from->udpHCInDatagrams;
382 	to->udpInErrors += from->udpInErrors;
383 	to->udpHCOutDatagrams += from->udpHCOutDatagrams;
384 	to->udpOutErrors += from->udpOutErrors;
385 }
386 
387 
388 void *
389 udp_kstat2_init(netstackid_t stackid)
390 {
391 	kstat_t *ksp;
392 
393 	udp_stat_t template = {
394 		{ "udp_sock_fallback",		KSTAT_DATA_UINT64 },
395 		{ "udp_out_opt",		KSTAT_DATA_UINT64 },
396 		{ "udp_out_err_notconn",	KSTAT_DATA_UINT64 },
397 		{ "udp_out_err_output",		KSTAT_DATA_UINT64 },
398 		{ "udp_out_err_tudr",		KSTAT_DATA_UINT64 },
399 #ifdef DEBUG
400 		{ "udp_data_conn",		KSTAT_DATA_UINT64 },
401 		{ "udp_data_notconn",		KSTAT_DATA_UINT64 },
402 		{ "udp_out_lastdst",		KSTAT_DATA_UINT64 },
403 		{ "udp_out_diffdst",		KSTAT_DATA_UINT64 },
404 		{ "udp_out_ipv6",		KSTAT_DATA_UINT64 },
405 		{ "udp_out_mapped",		KSTAT_DATA_UINT64 },
406 		{ "udp_out_ipv4",		KSTAT_DATA_UINT64 },
407 #endif
408 	};
409 
410 	ksp = kstat_create_netstack(UDP_MOD_NAME, 0, "udpstat", "net",
411 	    KSTAT_TYPE_NAMED, sizeof (template) / sizeof (kstat_named_t),
412 	    0, stackid);
413 
414 	if (ksp == NULL)
415 		return (NULL);
416 
417 	bcopy(&template, ksp->ks_data, sizeof (template));
418 	ksp->ks_update = udp_kstat2_update;
419 	ksp->ks_private = (void *)(uintptr_t)stackid;
420 
421 	kstat_install(ksp);
422 	return (ksp);
423 }
424 
425 void
426 udp_kstat2_fini(netstackid_t stackid, kstat_t *ksp)
427 {
428 	if (ksp != NULL) {
429 		ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
430 		kstat_delete_netstack(ksp, stackid);
431 	}
432 }
433 
434 /*
435  * To copy counters from the per CPU udpp_stat_counter_t to the stack
436  * udp_stat_t.
437  */
438 static void
439 udp_add_stats(udp_stat_counter_t *from, udp_stat_t *to)
440 {
441 	to->udp_sock_fallback.value.ui64 += from->udp_sock_fallback;
442 	to->udp_out_opt.value.ui64 += from->udp_out_opt;
443 	to->udp_out_err_notconn.value.ui64 += from->udp_out_err_notconn;
444 	to->udp_out_err_output.value.ui64 += from->udp_out_err_output;
445 	to->udp_out_err_tudr.value.ui64 += from->udp_out_err_tudr;
446 #ifdef DEBUG
447 	to->udp_data_conn.value.ui64 += from->udp_data_conn;
448 	to->udp_data_notconn.value.ui64 += from->udp_data_notconn;
449 	to->udp_out_lastdst.value.ui64 += from->udp_out_lastdst;
450 	to->udp_out_diffdst.value.ui64 += from->udp_out_diffdst;
451 	to->udp_out_ipv6.value.ui64 += from->udp_out_ipv6;
452 	to->udp_out_mapped.value.ui64 += from->udp_out_mapped;
453 	to->udp_out_ipv4.value.ui64 += from->udp_out_ipv4;
454 #endif
455 }
456 
457 /*
458  * To set all udp_stat_t counters to 0.
459  */
460 static void
461 udp_clr_stats(udp_stat_t *stats)
462 {
463 	stats->udp_sock_fallback.value.ui64 = 0;
464 	stats->udp_out_opt.value.ui64 = 0;
465 	stats->udp_out_err_notconn.value.ui64 = 0;
466 	stats->udp_out_err_output.value.ui64 = 0;
467 	stats->udp_out_err_tudr.value.ui64 = 0;
468 #ifdef DEBUG
469 	stats->udp_data_conn.value.ui64 = 0;
470 	stats->udp_data_notconn.value.ui64 = 0;
471 	stats->udp_out_lastdst.value.ui64 = 0;
472 	stats->udp_out_diffdst.value.ui64 = 0;
473 	stats->udp_out_ipv6.value.ui64 = 0;
474 	stats->udp_out_mapped.value.ui64 = 0;
475 	stats->udp_out_ipv4.value.ui64 = 0;
476 #endif
477 }
478 
479 int
480 udp_kstat2_update(kstat_t *kp, int rw)
481 {
482 	udp_stat_t	*stats;
483 	netstackid_t	stackid = (netstackid_t)(uintptr_t)kp->ks_private;
484 	netstack_t	*ns;
485 	udp_stack_t	*us;
486 	int		i;
487 	int		cnt;
488 
489 	if (rw == KSTAT_WRITE)
490 		return (EACCES);
491 
492 	ns = netstack_find_by_stackid(stackid);
493 	if (ns == NULL)
494 		return (-1);
495 	us = ns->netstack_udp;
496 	if (us == NULL) {
497 		netstack_rele(ns);
498 		return (-1);
499 	}
500 	stats = (udp_stat_t *)kp->ks_data;
501 	udp_clr_stats(stats);
502 
503 	cnt = us->us_sc_cnt;
504 	for (i = 0; i < cnt; i++)
505 		udp_add_stats(&us->us_sc[i]->udp_sc_stats, stats);
506 
507 	netstack_rele(ns);
508 	return (0);
509 }
510 
511 void *
512 udp_kstat_init(netstackid_t stackid)
513 {
514 	kstat_t	*ksp;
515 
516 	udp_named_kstat_t template = {
517 		{ "inDatagrams",	KSTAT_DATA_UINT64, 0 },
518 		{ "inErrors",		KSTAT_DATA_UINT32, 0 },
519 		{ "outDatagrams",	KSTAT_DATA_UINT64, 0 },
520 		{ "entrySize",		KSTAT_DATA_INT32, 0 },
521 		{ "entry6Size",		KSTAT_DATA_INT32, 0 },
522 		{ "outErrors",		KSTAT_DATA_UINT32, 0 },
523 	};
524 
525 	ksp = kstat_create_netstack(UDP_MOD_NAME, 0, UDP_MOD_NAME, "mib2",
526 	    KSTAT_TYPE_NAMED, NUM_OF_FIELDS(udp_named_kstat_t), 0, stackid);
527 
528 	if (ksp == NULL)
529 		return (NULL);
530 
531 	template.entrySize.value.ui32 = sizeof (mib2_udpEntry_t);
532 	template.entry6Size.value.ui32 = sizeof (mib2_udp6Entry_t);
533 
534 	bcopy(&template, ksp->ks_data, sizeof (template));
535 	ksp->ks_update = udp_kstat_update;
536 	ksp->ks_private = (void *)(uintptr_t)stackid;
537 
538 	kstat_install(ksp);
539 	return (ksp);
540 }
541 
542 /*
543  * To sum up all MIB2 stats for a udp_stack_t from all per CPU stats.  The
544  * caller should initialize the target mib2_udp_t properly as this function
545  * just adds up all the per CPU stats.
546  */
547 static void
548 udp_sum_mib(udp_stack_t *us, mib2_udp_t *udp_mib)
549 {
550 	int i;
551 	int cnt;
552 
553 	cnt = us->us_sc_cnt;
554 	for (i = 0; i < cnt; i++)
555 		udp_add_mib(&us->us_sc[i]->udp_sc_mib, udp_mib);
556 }
557 
558 static int
559 udp_kstat_update(kstat_t *kp, int rw)
560 {
561 	udp_named_kstat_t *udpkp;
562 	netstackid_t	stackid = (netstackid_t)(uintptr_t)kp->ks_private;
563 	netstack_t	*ns;
564 	udp_stack_t	*us;
565 	mib2_udp_t	udp_mib;
566 
567 	if (rw == KSTAT_WRITE)
568 		return (EACCES);
569 
570 	ns = netstack_find_by_stackid(stackid);
571 	if (ns == NULL)
572 		return (-1);
573 	us = ns->netstack_udp;
574 	if (us == NULL) {
575 		netstack_rele(ns);
576 		return (-1);
577 	}
578 	udpkp = (udp_named_kstat_t *)kp->ks_data;
579 
580 	bzero(&udp_mib, sizeof (udp_mib));
581 	udp_sum_mib(us, &udp_mib);
582 
583 	udpkp->inDatagrams.value.ui64 =	udp_mib.udpHCInDatagrams;
584 	udpkp->inErrors.value.ui32 =	udp_mib.udpInErrors;
585 	udpkp->outDatagrams.value.ui64 = udp_mib.udpHCOutDatagrams;
586 	udpkp->outErrors.value.ui32 =	udp_mib.udpOutErrors;
587 	netstack_rele(ns);
588 	return (0);
589 }
590