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.
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.
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]
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <sys/types.h>
27 #include <sys/tihdr.h>
28 #include <sys/policy.h>
30 #include <inet/common.h>
31 #include <inet/kstatcom.h>
32 #include <inet/snmpcom.h>
33 #include <inet/mib2.h>
34 #include <inet/optcom.h>
35 #include <inet/snmpcom.h>
36 #include <inet/kstatcom.h>
37 #include <inet/udp_impl.h>
39 static int udp_kstat_update(kstat_t
*, int);
40 static int udp_kstat2_update(kstat_t
*, int);
41 static void udp_sum_mib(udp_stack_t
*, mib2_udp_t
*);
42 static void udp_clr_stats(udp_stat_t
*);
43 static void udp_add_stats(udp_stat_counter_t
*, udp_stat_t
*);
44 static void udp_add_mib(mib2_udp_t
*, mib2_udp_t
*);
46 * return SNMP stuff in buffer in mpdata. We don't hold any lock and report
47 * information that can be changing beneath us.
50 udp_snmp_get(queue_t
*q
, mblk_t
*mpctl
, boolean_t legacy_req
)
56 mblk_t
*mp6_conn_tail
;
59 mib2_udp6Entry_t ude6
;
64 conn_t
*connp
= Q_TO_CONN(q
);
68 ip_stack_t
*ipst
= connp
->conn_netstack
->netstack_ip
;
69 udp_stack_t
*us
= connp
->conn_netstack
->netstack_udp
;
72 size_t udp_mib_size
, ude_size
, ude6_size
;
76 * make a copy of the original message
78 mp2ctl
= copymsg(mpctl
);
80 mp_conn_ctl
= mp6_conn_ctl
= NULL
;
82 (mpdata
= mpctl
->b_cont
) == NULL
||
83 (mp_conn_ctl
= copymsg(mpctl
)) == NULL
||
84 (mp6_conn_ctl
= copymsg(mpctl
)) == NULL
) {
86 freemsg(mp6_conn_ctl
);
92 zoneid
= connp
->conn_zoneid
;
95 udp_mib_size
= LEGACY_MIB_SIZE(&udp_mib
, mib2_udp_t
);
96 ude_size
= LEGACY_MIB_SIZE(&ude
, mib2_udpEntry_t
);
97 ude6_size
= LEGACY_MIB_SIZE(&ude6
, mib2_udp6Entry_t
);
99 udp_mib_size
= sizeof (mib2_udp_t
);
100 ude_size
= sizeof (mib2_udpEntry_t
);
101 ude6_size
= sizeof (mib2_udp6Entry_t
);
104 bzero(&udp_mib
, sizeof (udp_mib
));
105 /* fixed length structure for IPv4 and IPv6 counters */
106 SET_MIB(udp_mib
.udpEntrySize
, ude_size
);
107 SET_MIB(udp_mib
.udp6EntrySize
, ude6_size
);
109 udp_sum_mib(us
, &udp_mib
);
112 * Synchronize 32- and 64-bit counters. Note that udpInDatagrams and
113 * udpOutDatagrams are not updated anywhere in UDP. The new 64 bits
114 * counters are used. Hence the old counters' values in us_sc_mib
117 SYNC32_MIB(&udp_mib
, udpInDatagrams
, udpHCInDatagrams
);
118 SYNC32_MIB(&udp_mib
, udpOutDatagrams
, udpHCOutDatagrams
);
120 optp
= (struct opthdr
*)&mpctl
->b_rptr
[sizeof (struct T_optmgmt_ack
)];
121 optp
->level
= MIB2_UDP
;
123 (void) snmp_append_data(mpdata
, (char *)&udp_mib
, udp_mib_size
);
124 optp
->len
= msgdsize(mpdata
);
127 mp_conn_tail
= mp6_conn_tail
= NULL
;
128 v4_conn_idx
= v6_conn_idx
= 0;
130 for (i
= 0; i
< CONN_G_HASH_SIZE
; i
++) {
131 connfp
= &ipst
->ips_ipcl_globalhash_fanout
[i
];
134 while ((connp
= ipcl_get_next_conn(connfp
, connp
,
136 udp
= connp
->conn_udp
;
137 if (zoneid
!= connp
->conn_zoneid
)
141 * Note that the port numbers are sent in
145 if (udp
->udp_state
== TS_UNBND
)
146 state
= MIB2_UDP_unbound
;
147 else if (udp
->udp_state
== TS_IDLE
)
148 state
= MIB2_UDP_idle
;
149 else if (udp
->udp_state
== TS_DATA_XFER
)
150 state
= MIB2_UDP_connected
;
152 state
= MIB2_UDP_unknown
;
155 * Create an IPv4 table entry for IPv4 entries and also
156 * any IPv6 entries which are bound to in6addr_any
157 * (i.e. anything a IPv4 peer could connect/send to).
159 if (connp
->conn_ipversion
== IPV4_VERSION
||
160 (udp
->udp_state
<= TS_IDLE
&&
161 IN6_IS_ADDR_UNSPECIFIED(&connp
->conn_laddr_v6
))) {
162 ude
.udpEntryInfo
.ue_state
= state
;
164 * If in6addr_any this will set it to
167 ude
.udpLocalAddress
= connp
->conn_laddr_v4
;
168 ude
.udpLocalPort
= ntohs(connp
->conn_lport
);
169 if (udp
->udp_state
== TS_DATA_XFER
) {
171 * Can potentially get here for
172 * v6 socket if another process
173 * (say, ping) has just done a
174 * sendto(), changing the state
175 * from the TS_IDLE above to
176 * TS_DATA_XFER by the time we hit
177 * this part of the code.
179 ude
.udpEntryInfo
.ue_RemoteAddress
=
180 connp
->conn_faddr_v4
;
181 ude
.udpEntryInfo
.ue_RemotePort
=
182 ntohs(connp
->conn_fport
);
184 ude
.udpEntryInfo
.ue_RemoteAddress
= 0;
185 ude
.udpEntryInfo
.ue_RemotePort
= 0;
189 * We make the assumption that all udp_t
190 * structs will be created within an address
191 * region no larger than 32-bits.
193 ude
.udpInstance
= (uint32_t)(uintptr_t)udp
;
194 ude
.udpCreationProcess
=
195 (connp
->conn_cpid
< 0) ?
196 MIB2_UNKNOWN_PROCESS
:
198 ude
.udpCreationTime
= connp
->conn_open_time
;
200 (void) snmp_append_data2(mp_conn_ctl
->b_cont
,
201 &mp_conn_tail
, (char *)&ude
, ude_size
);
203 if (connp
->conn_ipversion
== IPV6_VERSION
) {
204 ude6
.udp6EntryInfo
.ue_state
= state
;
205 ude6
.udp6LocalAddress
= connp
->conn_laddr_v6
;
206 ude6
.udp6LocalPort
= ntohs(connp
->conn_lport
);
207 mutex_enter(&connp
->conn_lock
);
208 if (connp
->conn_ixa
->ixa_flags
&
211 connp
->conn_ixa
->ixa_scopeid
;
213 ude6
.udp6IfIndex
= connp
->conn_bound_if
;
215 mutex_exit(&connp
->conn_lock
);
216 if (udp
->udp_state
== TS_DATA_XFER
) {
217 ude6
.udp6EntryInfo
.ue_RemoteAddress
=
218 connp
->conn_faddr_v6
;
219 ude6
.udp6EntryInfo
.ue_RemotePort
=
220 ntohs(connp
->conn_fport
);
222 ude6
.udp6EntryInfo
.ue_RemoteAddress
=
224 ude6
.udp6EntryInfo
.ue_RemotePort
= 0;
227 * We make the assumption that all udp_t
228 * structs will be created within an address
229 * region no larger than 32-bits.
231 ude6
.udp6Instance
= (uint32_t)(uintptr_t)udp
;
232 ude6
.udp6CreationProcess
=
233 (connp
->conn_cpid
< 0) ?
234 MIB2_UNKNOWN_PROCESS
:
236 ude6
.udp6CreationTime
= connp
->conn_open_time
;
238 (void) snmp_append_data2(mp6_conn_ctl
->b_cont
,
239 &mp6_conn_tail
, (char *)&ude6
, ude6_size
);
244 /* IPv4 UDP endpoints */
245 optp
= (struct opthdr
*)&mp_conn_ctl
->b_rptr
[
246 sizeof (struct T_optmgmt_ack
)];
247 optp
->level
= MIB2_UDP
;
248 optp
->name
= MIB2_UDP_ENTRY
;
249 optp
->len
= msgdsize(mp_conn_ctl
->b_cont
);
250 qreply(q
, mp_conn_ctl
);
252 /* IPv6 UDP endpoints */
253 optp
= (struct opthdr
*)&mp6_conn_ctl
->b_rptr
[
254 sizeof (struct T_optmgmt_ack
)];
255 optp
->level
= MIB2_UDP6
;
256 optp
->name
= MIB2_UDP6_ENTRY
;
257 optp
->len
= msgdsize(mp6_conn_ctl
->b_cont
);
258 qreply(q
, mp6_conn_ctl
);
264 * Return 0 if invalid set request, 1 otherwise, including non-udp requests.
265 * NOTE: Per MIB-II, UDP has no writable data.
266 * TODO: If this ever actually tries to set anything, it needs to be
267 * to do the appropriate locking.
271 udp_snmp_set(queue_t
*q
, t_scalar_t level
, t_scalar_t name
,
272 uchar_t
*ptr
, int len
)
283 udp_kstat_fini(netstackid_t stackid
, kstat_t
*ksp
)
286 ASSERT(stackid
== (netstackid_t
)(uintptr_t)ksp
->ks_private
);
287 kstat_delete_netstack(ksp
, stackid
);
292 * To add stats from one mib2_udp_t to another. Static fields are not added.
293 * The caller should set them up propertly.
296 udp_add_mib(mib2_udp_t
*from
, mib2_udp_t
*to
)
298 to
->udpHCInDatagrams
+= from
->udpHCInDatagrams
;
299 to
->udpInErrors
+= from
->udpInErrors
;
300 to
->udpHCOutDatagrams
+= from
->udpHCOutDatagrams
;
301 to
->udpOutErrors
+= from
->udpOutErrors
;
306 udp_kstat2_init(netstackid_t stackid
)
310 udp_stat_t
template = {
311 { "udp_sock_fallback", KSTAT_DATA_UINT64
},
312 { "udp_out_opt", KSTAT_DATA_UINT64
},
313 { "udp_out_err_notconn", KSTAT_DATA_UINT64
},
314 { "udp_out_err_output", KSTAT_DATA_UINT64
},
315 { "udp_out_err_tudr", KSTAT_DATA_UINT64
},
317 { "udp_data_conn", KSTAT_DATA_UINT64
},
318 { "udp_data_notconn", KSTAT_DATA_UINT64
},
319 { "udp_out_lastdst", KSTAT_DATA_UINT64
},
320 { "udp_out_diffdst", KSTAT_DATA_UINT64
},
321 { "udp_out_ipv6", KSTAT_DATA_UINT64
},
322 { "udp_out_mapped", KSTAT_DATA_UINT64
},
323 { "udp_out_ipv4", KSTAT_DATA_UINT64
},
327 ksp
= kstat_create_netstack(UDP_MOD_NAME
, 0, "udpstat", "net",
328 KSTAT_TYPE_NAMED
, sizeof (template) / sizeof (kstat_named_t
),
334 bcopy(&template, ksp
->ks_data
, sizeof (template));
335 ksp
->ks_update
= udp_kstat2_update
;
336 ksp
->ks_private
= (void *)(uintptr_t)stackid
;
343 udp_kstat2_fini(netstackid_t stackid
, kstat_t
*ksp
)
346 ASSERT(stackid
== (netstackid_t
)(uintptr_t)ksp
->ks_private
);
347 kstat_delete_netstack(ksp
, stackid
);
352 * To copy counters from the per CPU udpp_stat_counter_t to the stack
356 udp_add_stats(udp_stat_counter_t
*from
, udp_stat_t
*to
)
358 to
->udp_sock_fallback
.value
.ui64
+= from
->udp_sock_fallback
;
359 to
->udp_out_opt
.value
.ui64
+= from
->udp_out_opt
;
360 to
->udp_out_err_notconn
.value
.ui64
+= from
->udp_out_err_notconn
;
361 to
->udp_out_err_output
.value
.ui64
+= from
->udp_out_err_output
;
362 to
->udp_out_err_tudr
.value
.ui64
+= from
->udp_out_err_tudr
;
364 to
->udp_data_conn
.value
.ui64
+= from
->udp_data_conn
;
365 to
->udp_data_notconn
.value
.ui64
+= from
->udp_data_notconn
;
366 to
->udp_out_lastdst
.value
.ui64
+= from
->udp_out_lastdst
;
367 to
->udp_out_diffdst
.value
.ui64
+= from
->udp_out_diffdst
;
368 to
->udp_out_ipv6
.value
.ui64
+= from
->udp_out_ipv6
;
369 to
->udp_out_mapped
.value
.ui64
+= from
->udp_out_mapped
;
370 to
->udp_out_ipv4
.value
.ui64
+= from
->udp_out_ipv4
;
375 * To set all udp_stat_t counters to 0.
378 udp_clr_stats(udp_stat_t
*stats
)
380 stats
->udp_sock_fallback
.value
.ui64
= 0;
381 stats
->udp_out_opt
.value
.ui64
= 0;
382 stats
->udp_out_err_notconn
.value
.ui64
= 0;
383 stats
->udp_out_err_output
.value
.ui64
= 0;
384 stats
->udp_out_err_tudr
.value
.ui64
= 0;
386 stats
->udp_data_conn
.value
.ui64
= 0;
387 stats
->udp_data_notconn
.value
.ui64
= 0;
388 stats
->udp_out_lastdst
.value
.ui64
= 0;
389 stats
->udp_out_diffdst
.value
.ui64
= 0;
390 stats
->udp_out_ipv6
.value
.ui64
= 0;
391 stats
->udp_out_mapped
.value
.ui64
= 0;
392 stats
->udp_out_ipv4
.value
.ui64
= 0;
397 udp_kstat2_update(kstat_t
*kp
, int rw
)
400 netstackid_t stackid
= (netstackid_t
)(uintptr_t)kp
->ks_private
;
406 if (rw
== KSTAT_WRITE
)
409 ns
= netstack_find_by_stackid(stackid
);
412 us
= ns
->netstack_udp
;
417 stats
= (udp_stat_t
*)kp
->ks_data
;
418 udp_clr_stats(stats
);
421 for (i
= 0; i
< cnt
; i
++)
422 udp_add_stats(&us
->us_sc
[i
]->udp_sc_stats
, stats
);
429 udp_kstat_init(netstackid_t stackid
)
433 udp_named_kstat_t
template = {
434 { "inDatagrams", KSTAT_DATA_UINT64
, 0 },
435 { "inErrors", KSTAT_DATA_UINT32
, 0 },
436 { "outDatagrams", KSTAT_DATA_UINT64
, 0 },
437 { "entrySize", KSTAT_DATA_INT32
, 0 },
438 { "entry6Size", KSTAT_DATA_INT32
, 0 },
439 { "outErrors", KSTAT_DATA_UINT32
, 0 },
442 ksp
= kstat_create_netstack(UDP_MOD_NAME
, 0, UDP_MOD_NAME
, "mib2",
443 KSTAT_TYPE_NAMED
, NUM_OF_FIELDS(udp_named_kstat_t
), 0, stackid
);
448 template.entrySize
.value
.ui32
= sizeof (mib2_udpEntry_t
);
449 template.entry6Size
.value
.ui32
= sizeof (mib2_udp6Entry_t
);
451 bcopy(&template, ksp
->ks_data
, sizeof (template));
452 ksp
->ks_update
= udp_kstat_update
;
453 ksp
->ks_private
= (void *)(uintptr_t)stackid
;
460 * To sum up all MIB2 stats for a udp_stack_t from all per CPU stats. The
461 * caller should initialize the target mib2_udp_t properly as this function
462 * just adds up all the per CPU stats.
465 udp_sum_mib(udp_stack_t
*us
, mib2_udp_t
*udp_mib
)
471 for (i
= 0; i
< cnt
; i
++)
472 udp_add_mib(&us
->us_sc
[i
]->udp_sc_mib
, udp_mib
);
476 udp_kstat_update(kstat_t
*kp
, int rw
)
478 udp_named_kstat_t
*udpkp
;
479 netstackid_t stackid
= (netstackid_t
)(uintptr_t)kp
->ks_private
;
484 if (rw
== KSTAT_WRITE
)
487 ns
= netstack_find_by_stackid(stackid
);
490 us
= ns
->netstack_udp
;
495 udpkp
= (udp_named_kstat_t
*)kp
->ks_data
;
497 bzero(&udp_mib
, sizeof (udp_mib
));
498 udp_sum_mib(us
, &udp_mib
);
500 udpkp
->inDatagrams
.value
.ui64
= udp_mib
.udpHCInDatagrams
;
501 udpkp
->inErrors
.value
.ui32
= udp_mib
.udpInErrors
;
502 udpkp
->outDatagrams
.value
.ui64
= udp_mib
.udpHCOutDatagrams
;
503 udpkp
->outErrors
.value
.ui32
= udp_mib
.udpOutErrors
;