├── .gitignore
├── BUGS.md
├── ChangeLog.txt
├── LICENSE.txt
├── Makefile.Linux
├── Makefile.Solaris
├── README.md
├── README.txt
├── README_upstream.md
├── dladm.sh
├── enicstat
├── nicstat.1
├── nicstat.c
└── nicstat.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | nicstat
3 |
--------------------------------------------------------------------------------
/BUGS.md:
--------------------------------------------------------------------------------
1 | Bugs
2 | ====
3 |
4 | The following bugs are fixed in this fork of nicstat.
5 |
6 | Bug #1 - Utilization with 10GbE nics on RHEL is incorrect
7 | ---------------------------------------------------------
8 |
9 | This fixes http://sourceforge.net/p/nicstat/bugs/1/ by applying
10 | [the patch](https://github.com/scotte/nicstat/commit/3bd5866856ecf6a1312931851d4c5dcd0063d60a)
11 | provided by Darren in that that bug report.
12 |
13 | This bug occurs due to an integer overflow for 10Gb interfaces and
14 | is not specific to RHEL.
15 |
16 | Bug #3 - -S option ignored if SIOCETHTOOL succeeds
17 | --------------------------------------------------
18 |
19 | This fixes http://sourceforge.net/p/nicstat/bugs/3/ by applying
20 | [the patch](https://github.com/scotte/nicstat/commit/23c7de0b7c39b9b6ec27ab34e479afb48bcc6711)
21 | I provided in that that bug report.
22 |
23 | This patch may not be ideal, and should be revisited for completeness,
24 | but does resolve the issue for me.
25 |
26 | Bug #4 - Utilization always computed as half duplex
27 | ---------------------------------------------------
28 |
29 | This fixes http://sourceforge.net/p/nicstat/bugs/4/ by applying
30 | [the patch](https://github.com/scotte/nicstat/commit/9ea8c81c55e4cf7487c1eebbb2976b60b016fa02)
31 | I provided in that that bug report.
32 |
33 | When utilization is calculated, nicp->duplex checks for the value of
34 | "2" for a full duplex link, but on Linux this value is "1" (see
35 | /usr/include/linux/ethtool.h). Fix is to use DUPLEX_FULL #define.
36 |
37 | Bug #5 - Remove unnecessary #ifdef block
38 | ----------------------------------------
39 |
40 | This fixes http://sourceforge.net/p/nicstat/bugs/5/ by applying
41 | [the patch](https://github.com/scotte/nicstat/commit/303aea60db5b9ebc957b1bac368832b85ef88f94)
42 | I provided in that that bug report.
43 |
44 | An #ifdef for linux vs solaris is not needed as the DUPLEX_ #defines
45 | can be used consistently instead.
46 |
47 | Bug #6 - UDP stats scanf has too many arguments
48 | -----------------------------------------------
49 |
50 | This fixes http://sourceforge.net/p/nicstat/bugs/6/ by applying
51 | [the patch](https://github.com/scotte/nicstat/commit/d536743abb23fb1174b0f801a4eed7d9ceac4763)
52 | I provided in that that bug report.
53 |
54 | There were too many arguments to fscanf(), resulting in the following warning
55 | when compiling with -Wall:
56 |
57 | nicstat.c: In function ‘load_snmp’:
58 | nicstat.c:1569:8: warning: too many arguments for format [-Wformat-extra-args]
59 |
60 | Bug #7 - Remove unused variables
61 | --------------------------------
62 |
63 | This fixes http://sourceforge.net/p/nicstat/bugs/7/ by applying
64 | [the patch](https://github.com/scotte/nicstat/commit/f59393f19a4c7ca7c1dc8a873afe8cd6a1a4b036)
65 | I provided in that that bug report.
66 |
67 | There are two unused variables in Linux (one is used only for solaris,
68 | the other used nowhere), found when compiling with -Wall.
69 |
--------------------------------------------------------------------------------
/ChangeLog.txt:
--------------------------------------------------------------------------------
1 | Version 1.95
2 | January 2014
3 |
4 | Common
5 |
6 | - Added "-U" option, to display separate read and write
7 | utilization.
8 |
9 | - Simplified display code regarding "-M" option.
10 |
11 | Solaris
12 |
13 | - Fixed fetch64() to check type of kstats
14 |
15 | Version 1.93
16 | January 2014
17 |
18 | Solaris
19 |
20 | - Fixed memory leak in update_nicdata_list()
21 |
22 | Version 1.92
23 | October 2012
24 |
25 | Common
26 |
27 | - Added "-M" option to change throughput statistics to Mbps
28 | (Megabits per second). Suggestion from Darren Todd.
29 |
30 | - Fixed bugs with printing extended parseable format (-xp)
31 |
32 | - Fixed man page's description of extended parseable output.
33 |
34 | Solaris
35 |
36 | - Fixed memory leak associated with g_getif_list
37 |
38 | - Add 2nd argument to dladm_open() for Solaris 11.1
39 |
40 | - Modify nicstat.sh to handle Solaris 11.1
41 |
42 | Linux
43 |
44 | - Modify nicstat.sh to see "x86_64" cputype as "i386". All Linux
45 | binaries are built as 32-bit, so we do not need to
46 | differentiate these two cpu types.
47 |
48 | Version 1.90
49 | April 2011
50 |
51 | Common
52 |
53 | - nicstat.sh script, to provide for automated multi-platform
54 | deployment. See the Makefile's for details.
55 |
56 | - Added "-x" flag, to display extended statistics for each
57 | interface.
58 |
59 | - Added "-t" and "-u" flags, to include TCP and UDP
60 | (respectively) statistics. These come from tcp:0:tcpstat and
61 | udp:0:udpstat on Solaris, or from /proc/net/snmp and
62 | /proc/net/netstat on Linux.
63 |
64 | - Added "-a" flag, which equates to "-tux".
65 |
66 | - Added "-l" flag, which lists interfaces and their
67 | configuration.
68 |
69 | - Added "-v" flag, which displays nicstat version.
70 |
71 | Solaris
72 |
73 | - Added use of libdladm.so:dladm_walk_datalink_id() to get list
74 | of interfaces. This is better than SIOCGLIFCONF, as it
75 | includes interfaces given exclusively to a zone.
76 |
77 | NOTE: this library/routine is linked with nicstat in "lazy"
78 | mode, meaning that a Solaris 11 binary built with knowledge of
79 | the routine will also run on Solaris 10 without failing when
80 | the routine or library is not found - in this case nicstat
81 | will fall back to the SIOGLIFCONF method.
82 |
83 | - Added search of kstat "link_state" statistics as a third
84 | method for finding active network interfaces. See the man
85 | page for details.
86 |
87 | Linux
88 |
89 | - Added support for SIOCETHTOOL ioctl, so that nicstat can look
90 | up interface speed/duplex (i.e. "-S" flag not necessarily
91 | needed any longer).
92 |
93 | - Removed need for LLONG_MAX, improving Linux portability.
94 |
95 | Version 1.28c
96 | Feb 2010
97 |
98 | Common
99 |
100 | - Added "-p" flag, which produces "parseable" output.
101 | Compatible with System Data Recorder (SDR).
102 |
103 | Version 1.21
104 | Oct 2009
105 |
106 | Solaris
107 |
108 | - Now works correctly on Shared-IP zones.
109 |
110 | Version 1.20
111 | Sep 2009
112 |
113 | - Added support for a "fd" or "hd" (in reality anything starting
114 | with an upper or lower-case F or H) suffix to the speed
115 | settings supplied via the "-S" option. This advises nicstat
116 | the interface is half-duplex or full-duplex. The Linux version
117 | now calculates %Util the same way as the Solaris version.
118 |
119 | - Added a script, enicstat, which uses ethtool to get speeds and
120 | duplex modes for all interfaces, then calls nicstat with an
121 | appropriate -S value.
122 |
123 | - Made the Linux version more efficient.
124 |
125 | - Combined the Solaris and Linux source into one nicstat.c. This
126 | is a little ugly due to #ifdef's, but that's the price you
127 | pay.
128 |
129 | - Wrote a man page.
130 |
131 | - Wrote better Makefile's for both platforms
132 |
133 | - Wrote a short README
134 |
135 | - Licensed nicstat under the Artistic License 2.0
136 |
137 | Version 1.16
138 | Aug 2009
139 |
140 | A colleague pointed out to me that nicstat's method of
141 | calculating utilization for a full-duplex interface is not
142 | correct.
143 |
144 | Now nicstat will look for the kstat "link_duplex" value, and
145 | if it is 2 (which means full-duplex), it will use the greater
146 | of rbytes or wbytes to calculate utilization.
147 |
148 | Version 1.15
149 | Aug 2009
150 |
151 | Merged Linux and Solaris versions.
152 |
153 | Jul 2009
154 |
155 | A number of people have commented to me that nicstat always
156 | reports "0.00" for %Util on Linux. The reason for this is that
157 | there is no simple way an unprivileged user can get the speed
158 | of an interface in Linux (quite happy for someone to prove me
159 | wrong on that however).
160 |
161 | Recently I got an offer of a patch from David Stone, to add an
162 | option to nicstat that tells it what the speed of an interface
163 | is. Pretty reasonable idea, so I have added it to the Linux
164 | version. You will see this new "-S" option explained if you
165 | use nicstat's "-h" (help) option.
166 |
167 | I have made another change which makes nicstat more portable,
168 | hence easier to build on Linux.
169 |
170 | Feb 2007
171 |
172 | Wrote Linux version.
173 |
174 | Version 1.6
175 | Aug 2006
176 |
177 | Added "-n" flag (non-local).
178 |
179 | Added precision() function suggested by Brendan.
180 |
181 | Version 1.5
182 | Aug 2006
183 |
184 | Major re-write. First use of SIOCGLIFCONF to get list of
185 | active interfaces.
186 |
187 | Version 1.4
188 | Jul 2006
189 |
190 | First version to use gethrtime() and nanosleep() on Solaris to
191 | maintain better interval timing. This technique was later
192 | applied in CR 6611108.
193 |
194 | Some Version
195 | Sometime earlier than Jul 2006
196 |
197 | First additions by Tim Cook.
198 |
199 | Older Versions
200 | Even Earlier
201 |
202 | Original Perl and C versions by Brendan Gregg.
203 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The Artistic License 2.0
2 |
3 | Copyright (c) 2000-2006, The Perl Foundation.
4 |
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | This license establishes the terms under which a given free software
11 | Package may be copied, modified, distributed, and/or redistributed.
12 | The intent is that the Copyright Holder maintains some artistic
13 | control over the development of that Package while still keeping the
14 | Package available as open source and free software.
15 |
16 | You are always permitted to make arrangements wholly outside of this
17 | license directly with the Copyright Holder of a given Package. If the
18 | terms of this license do not permit the full use that you propose to
19 | make of the Package, you should contact the Copyright Holder and seek
20 | a different licensing arrangement.
21 |
22 | Definitions
23 |
24 | "Copyright Holder" means the individual(s) or organization(s)
25 | named in the copyright notice for the entire Package.
26 |
27 | "Contributor" means any party that has contributed code or other
28 | material to the Package, in accordance with the Copyright Holder's
29 | procedures.
30 |
31 | "You" and "your" means any person who would like to copy,
32 | distribute, or modify the Package.
33 |
34 | "Package" means the collection of files distributed by the
35 | Copyright Holder, and derivatives of that collection and/or of
36 | those files. A given Package may consist of either the Standard
37 | Version, or a Modified Version.
38 |
39 | "Distribute" means providing a copy of the Package or making it
40 | accessible to anyone else, or in the case of a company or
41 | organization, to others outside of your company or organization.
42 |
43 | "Distributor Fee" means any fee that you charge for Distributing
44 | this Package or providing support for this Package to another
45 | party. It does not mean licensing fees.
46 |
47 | "Standard Version" refers to the Package if it has not been
48 | modified, or has been modified only in ways explicitly requested
49 | by the Copyright Holder.
50 |
51 | "Modified Version" means the Package, if it has been changed, and
52 | such changes were not explicitly requested by the Copyright
53 | Holder.
54 |
55 | "Original License" means this Artistic License as Distributed with
56 | the Standard Version of the Package, in its current version or as
57 | it may be modified by The Perl Foundation in the future.
58 |
59 | "Source" form means the source code, documentation source, and
60 | configuration files for the Package.
61 |
62 | "Compiled" form means the compiled bytecode, object code, binary,
63 | or any other form resulting from mechanical transformation or
64 | translation of the Source form.
65 |
66 |
67 | Permission for Use and Modification Without Distribution
68 |
69 | (1) You are permitted to use the Standard Version and create and use
70 | Modified Versions for any purpose without restriction, provided that
71 | you do not Distribute the Modified Version.
72 |
73 |
74 | Permissions for Redistribution of the Standard Version
75 |
76 | (2) You may Distribute verbatim copies of the Source form of the
77 | Standard Version of this Package in any medium without restriction,
78 | either gratis or for a Distributor Fee, provided that you duplicate
79 | all of the original copyright notices and associated disclaimers. At
80 | your discretion, such verbatim copies may or may not include a
81 | Compiled form of the Package.
82 |
83 | (3) You may apply any bug fixes, portability changes, and other
84 | modifications made available from the Copyright Holder. The resulting
85 | Package will still be considered the Standard Version, and as such
86 | will be subject to the Original License.
87 |
88 |
89 | Distribution of Modified Versions of the Package as Source
90 |
91 | (4) You may Distribute your Modified Version as Source (either gratis
92 | or for a Distributor Fee, and with or without a Compiled form of the
93 | Modified Version) provided that you clearly document how it differs
94 | from the Standard Version, including, but not limited to, documenting
95 | any non-standard features, executables, or modules, and provided that
96 | you do at least ONE of the following:
97 |
98 | (a) make the Modified Version available to the Copyright Holder
99 | of the Standard Version, under the Original License, so that the
100 | Copyright Holder may include your modifications in the Standard
101 | Version.
102 |
103 | (b) ensure that installation of your Modified Version does not
104 | prevent the user installing or running the Standard Version. In
105 | addition, the Modified Version must bear a name that is different
106 | from the name of the Standard Version.
107 |
108 | (c) allow anyone who receives a copy of the Modified Version to
109 | make the Source form of the Modified Version available to others
110 | under
111 |
112 | (i) the Original License or
113 |
114 | (ii) a license that permits the licensee to freely copy,
115 | modify and redistribute the Modified Version using the same
116 | licensing terms that apply to the copy that the licensee
117 | received, and requires that the Source form of the Modified
118 | Version, and of any works derived from it, be made freely
119 | available in that license fees are prohibited but Distributor
120 | Fees are allowed.
121 |
122 |
123 | Distribution of Compiled Forms of the Standard Version
124 | or Modified Versions without the Source
125 |
126 | (5) You may Distribute Compiled forms of the Standard Version without
127 | the Source, provided that you include complete instructions on how to
128 | get the Source of the Standard Version. Such instructions must be
129 | valid at the time of your distribution. If these instructions, at any
130 | time while you are carrying out such distribution, become invalid, you
131 | must provide new instructions on demand or cease further distribution.
132 | If you provide valid instructions or cease distribution within thirty
133 | days after you become aware that the instructions are invalid, then
134 | you do not forfeit any of your rights under this license.
135 |
136 | (6) You may Distribute a Modified Version in Compiled form without
137 | the Source, provided that you comply with Section 4 with respect to
138 | the Source of the Modified Version.
139 |
140 |
141 | Aggregating or Linking the Package
142 |
143 | (7) You may aggregate the Package (either the Standard Version or
144 | Modified Version) with other packages and Distribute the resulting
145 | aggregation provided that you do not charge a licensing fee for the
146 | Package. Distributor Fees are permitted, and licensing fees for other
147 | components in the aggregation are permitted. The terms of this license
148 | apply to the use and Distribution of the Standard or Modified Versions
149 | as included in the aggregation.
150 |
151 | (8) You are permitted to link Modified and Standard Versions with
152 | other works, to embed the Package in a larger work of your own, or to
153 | build stand-alone binary or bytecode versions of applications that
154 | include the Package, and Distribute the result without restriction,
155 | provided the result does not expose a direct interface to the Package.
156 |
157 |
158 | Items That are Not Considered Part of a Modified Version
159 |
160 | (9) Works (including, but not limited to, modules and scripts) that
161 | merely extend or make use of the Package, do not, by themselves, cause
162 | the Package to be a Modified Version. In addition, such works are not
163 | considered parts of the Package itself, and are not subject to the
164 | terms of this license.
165 |
166 |
167 | General Provisions
168 |
169 | (10) Any use, modification, and distribution of the Standard or
170 | Modified Versions is governed by this Artistic License. By using,
171 | modifying or distributing the Package, you accept this license. Do not
172 | use, modify, or distribute the Package, if you do not accept this
173 | license.
174 |
175 | (11) If your Modified Version has been derived from a Modified
176 | Version made by someone other than you, you are nevertheless required
177 | to ensure that your Modified Version complies with the requirements of
178 | this license.
179 |
180 | (12) This license does not grant you the right to use any trademark,
181 | service mark, tradename, or logo of the Copyright Holder.
182 |
183 | (13) This license includes the non-exclusive, worldwide,
184 | free-of-charge patent license to make, have made, use, offer to sell,
185 | sell, import and otherwise transfer the Package with respect to any
186 | patent claims licensable by the Copyright Holder that are necessarily
187 | infringed by the Package. If you institute patent litigation
188 | (including a cross-claim or counterclaim) against any party alleging
189 | that the Package constitutes direct or contributory patent
190 | infringement, then this Artistic License to you shall terminate on the
191 | date that such litigation is filed.
192 |
193 | (14) Disclaimer of Warranty:
194 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
195 | IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
196 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
197 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
198 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
199 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
200 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
201 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
202 |
--------------------------------------------------------------------------------
/Makefile.Linux:
--------------------------------------------------------------------------------
1 | # Makefile.Linux - for nicstat, Linux platform edition
2 |
3 | FILES = nicstat
4 |
5 | SOURCES = nicstat.c
6 |
7 | BINARY = nicstat
8 | BINARIES = $(BINARY) enicstat
9 |
10 | CC = gcc
11 | #-- This may be useful on RHEL versions where gcc is only version 4.1
12 | #CC = gcc43
13 |
14 | #COPT = -g
15 | COPT = -O3
16 |
17 | # If you are building on (or for) a Linux distro that lacks support
18 | # for ILP32, you can usually build an LP64 executable by changing
19 | # CMODEL to -m64, or leaving it undefined (-m64 is the default on most
20 | # Linux distros).
21 |
22 | #CMODEL =
23 | CMODEL = -m32
24 |
25 | CFLAGS = $(COPT) $(CMODEL)
26 |
27 | INSTALL = sudo install -o bin -g bin
28 | SETUINSTALL = sudo install -o root -g root -m 4511
29 |
30 | #-- Change this for your OS. Look in nicstat.sh to see if you want
31 | #-- to be more specific
32 | OSTYPE = RedHat
33 | OSREL = 5
34 | CPUTYPE = i386
35 |
36 | #--------------------------------
37 |
38 | BASEDIR = /usr/local
39 | BINDIR = $(BASEDIR)/bin
40 | MANDIR = $(BASEDIR)/share/man
41 | MP_DIR = $(BINDIR)
42 |
43 | BINARY = nicstat
44 | #NATIVE_BINARY = .$(BINARY).$(OSTYPE)_$(OSREL)_$(CPUTYPE)
45 | NATIVE_BINARY = `./nicstat.sh --bin-name`
46 |
47 |
48 | all : $(FILES)
49 | mv $? $(NATIVE_BINARY)
50 |
51 | $(NATIVE_BINARY) : $(BINARY)
52 | mv $? $@
53 |
54 | #-- Choose one of these two install methods:
55 | install : install_native install_man
56 | #install : install_multi_platform install_man
57 |
58 | install_native : $(BINARIES)
59 | $(SETUINSTALL) $(NATIVE_BINARY) $(BINDIR)/$(BINARY)
60 | $(INSTALL) -m 555 enicstat $(BINDIR)
61 |
62 | #
63 | # You may need to tweak the chown/chmod commands - all Linux
64 | # binaries need setuid-root if they are to use the SIOCETHTOOL ioctl
65 | # (which is optional, see the man page)
66 | #
67 | install_multi_platform : $(NATIVE_BINARY) enicstat
68 | $(INSTALL) -m 755 nicstat.sh $(BINDIR)/nicstat
69 | $(INSTALL) -m 555 enicstat $(BINDIR)
70 | sudo cp -p .nicstat.* $(MP_DIR)
71 | sudo chown root:bin $(MP_DIR)/.nicstat.Linux*
72 | sudo chmod 4711 $(MP_DIR)/.nicstat.Linux*
73 |
74 | install_man: nicstat.1
75 | $(INSTALL) -m 444 nicstat.1 $(MANDIR)/man1/nicstat.1
76 |
77 | lint :
78 | lint $(SOURCES) $(LDLIBS)
79 |
80 | clean :
81 | rm -f $(FILES)
82 |
--------------------------------------------------------------------------------
/Makefile.Solaris:
--------------------------------------------------------------------------------
1 | # Makefile.Solaris - for nicstat, Solaris platform Edition
2 |
3 | FILES = nicstat
4 |
5 | SOURCES = nicstat.c
6 |
7 | BINARY = nicstat
8 | BINARIES = $(BINARY)
9 |
10 | COPT = -g -xO3
11 | #COPT = -g
12 |
13 | # _REENTRANT gets us the declaration of strtok_r() on Solaris 9
14 | CFLAGS = $(COPT) -D_REENTRANT `./dladm.sh def`
15 |
16 | LDLIBS = -lsocket -lkstat -lrt `./dladm.sh lib`
17 |
18 | FILES = nicstat
19 |
20 | SOURCES = nicstat.c
21 |
22 | SU = pfexec
23 | INSTALL = $(SU) /usr/sbin/install -u root -g bin
24 | SETUINSTALL = $(INSTALL)
25 |
26 | #--------------------------------
27 |
28 | FILES = nicstat
29 |
30 | SOURCES = nicstat.c
31 |
32 | BASEDIR = /usr/local
33 | BINDIR = $(BASEDIR)/bin
34 | MANDIR = $(BASEDIR)/share/man
35 | MP_DIR = $(BINDIR)
36 |
37 | BINARY = nicstat
38 | NATIVE_BINARY = `./nicstat.sh --bin-name`
39 |
40 |
41 | all : $(FILES)
42 | mv $? $(NATIVE_BINARY)
43 |
44 | $(NATIVE_BINARY) : $(BINARY)
45 | mv $? $@
46 |
47 | #-- Choose one of these two install methods:
48 | install : install_native install_man
49 | #install : install_multi_platform install_man
50 |
51 | install_native : $(BINARIES)
52 | $(INSTALL) $(NATIVE_BINARY) $(BINDIR)/$(BINARY)
53 | # $(INSTALL) -m 555 enicstat $(BINDIR)
54 |
55 | #
56 | # You may need to tweak the chown/chmod commands - all Linux
57 | # binaries need setuid-root if they are to use the SIOCETHTOOL ioctl
58 | # (which is optional, see the man page)
59 | #
60 | install_multi_platform : $(NATIVE_BINARY) enicstat
61 | $(INSTALL) -m 755 nicstat.sh $(BINDIR)/nicstat
62 | $(INSTALL) -m 555 enicstat $(BINDIR)
63 | sudo cp -p .nicstat.* $(MP_DIR)
64 | sudo chown root:bin $(MP_DIR)/.nicstat.Linux*
65 | sudo chmod 4711 $(MP_DIR)/.nicstat.Linux*
66 |
67 | install_man: nicstat.1
68 | $(INSTALL) -m 444 nicstat.1 $(MANDIR)/man1/nicstat.1
69 |
70 | lint :
71 | lint $(SOURCES) $(LDLIBS)
72 |
73 | clean :
74 | rm -f $(FILES)
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | nicstat
2 | =======
3 |
4 | nicstat was written by Brendan Gregg and Tim Cook of Sun Microsystems - originally
5 | for Solaris, ported to Linux.
6 |
7 | The official, upstream repository for nicstat is at https://sourceforge.net/projects/nicstat/
8 |
9 | This fork
10 | =========
11 |
12 | This fork exists primarily to address some bugs in the upstream code. See
13 | [BUGS.md](BUGS.md) for a list of fixed bugs.
14 |
15 | The original, imported source is on the
16 | [upstream_1.95](https://github.com/scotte/nicstat/tree/upstream_1.95)
17 | branch of this repository.
18 |
19 | This fork has only been tested on Linux. Reasonable effort has been made that
20 | it hasn't been broken in Solaris, but it is possible. Please create a Github
21 | issue if any issues are found (or - even better - a pull request with a fix!).
22 | Thanks.
23 |
24 | If any changes are made to the upstream source, I will merge them in in an
25 | attempt to keep this tree in sync with upstream.
26 |
27 | Pull requests are welcome for any additional bugs or features as well.
28 |
29 | License
30 | =======
31 |
32 | nicstat is entirely the property of its originators. All rights, restrictions,
33 | limitations, warranties, etc remain per nicstat's owners and license.
34 |
35 | nicstat is licensed under the Artistic License 2.0. You can find a
36 | copy of this license as [LICENSE.txt](LICENSE.txt) included with the nicstat
37 | distribution, or at http://www.perlfoundation.org/artistic_license_2_0
38 |
39 | README_upstream.md
40 | ==================
41 |
42 | Following is the full contents of [README_upstream.md](README_upstream.md),
43 | which is **README.md** in the sourceforge file listing (but not part of the
44 | source tarball):
45 |
46 | * * *
47 |
48 | nicstat is a Solaris and Linux command-line that prints out network
49 | statistics for all network interface cards (NICs), including packets,
50 | kilobytes per second, average packet sizes and more.
51 |
52 | It was developed by Tim Cook and Brendan Gregg, both formerly of Sun
53 | Microsystems.
54 |
55 | Changes for Version 1.95, January 2014
56 | --------------------------------------
57 |
58 | ## Common
59 |
60 | - Added "-U" option, to display separate read and write
61 | utilization.
62 |
63 | - Simplified display code regarding "-M" option.
64 |
65 | ## Solaris
66 |
67 | - Fixed fetch64() to check type of kstats
68 |
69 | - Fixed memory leak in update_nicdata_list()
70 |
71 | Changes for Version 1.92, October 2012
72 | --------------------------------------
73 |
74 | ## Common
75 |
76 | - Added "-M" option to change throughput statistics to Mbps
77 | (Megabits per second). Suggestion from Darren Todd.
78 |
79 | - Fixed bugs with printing extended parseable format (-xp)
80 |
81 | - Fixed man page's description of extended parseable output.
82 |
83 | ## Solaris
84 |
85 | - Fixed memory leak associated with g_getif_list
86 |
87 | - Add 2nd argument to dladm_open() for Solaris 11.1
88 |
89 | - Modify nicstat.sh to handle Solaris 11.1
90 |
91 | ## Linux
92 |
93 | - Modify nicstat.sh to see "x86_64" cputype as "i386". All Linux
94 | binaries are built as 32-bit, so we do not need to differentiate
95 | these two cpu types.
96 |
97 | Changes for Version 1.90, April 2011
98 | ------------------------------------
99 |
100 | ## Common
101 |
102 | - nicstat.sh script, to provide for automated multi-platform
103 | deployment. See the Makefile's for details.
104 |
105 | - Added "-x" flag, to display extended statistics for each
106 | interface.
107 |
108 | - Added "-t" and "-u" flags, to include TCP and UDP
109 | (respectively) statistics. These come from tcp:0:tcpstat
110 | and udp:0:udpstat on Solaris, or from /proc/net/snmp and
111 | /proc/net/netstat on Linux.
112 |
113 | - Added "-a" flag, which equates to "-tux".
114 |
115 | - Added "-l" flag, which lists interfaces and their
116 | configuration.
117 |
118 | - Added "-v" flag, which displays nicstat version.
119 |
120 | ## Solaris
121 |
122 | - Added use of libdladm.so:dladm_walk_datalink_id() to get list of
123 | interfaces. This is better than SIOCGLIFCONF, as it includes
124 | interfaces given exclusively to a zone.
125 | NOTE: this library/routine can be linked in to nicstat in "lazy"
126 | mode, meaning that a Solaris 11 binary built with knowledge of the
127 | routine will also run on Solaris 10 without failing when the routine
128 | or library is not found - in this case nicstat will fall back to the
129 | SIOGLIFCONF method.
130 |
131 | - Added search of kstat "link_state" statistics as a third
132 | method for finding active network interfaces. See the man
133 | page for details.
134 |
135 | ## Linux
136 |
137 | - Added support for SIOCETHTOOL ioctl, so that nicstat can
138 | look up interface speed/duplex (i.e. "-S" flag not necessarily
139 | needed any longer).
140 |
141 | - Removed need for LLONG_MAX, improving Linux portability.
142 |
143 | * * *
144 |
145 | README.txt
146 | ==========
147 |
148 | Following is the full contents of [README.txt](README.txt):
149 |
150 | ```
151 | nicstat 1.95 README
152 | ===================
153 |
154 | nicstat is licensed under the Artistic License 2.0. You can find a
155 | copy of this license as LICENSE.txt included with the nicstat
156 | distribution, or at http://www.perlfoundation.org/artistic_license_2_0
157 |
158 |
159 | AUTHORS
160 | timothy.cook@oracle.com (formerly tim.cook@sun.com), Brendan Gregg
161 | (formerly Brendan.Gregg@sun.com)
162 |
163 | HOW TO BUILD ON SOLARIS
164 | mv Makefile.Solaris Makefile
165 | make
166 |
167 | HOW TO BUILD ON LINUX
168 | mv Makefile.Linux Makefile
169 | make
170 |
171 | HOW TO INSTALL
172 | make [BASEDIR=
] install
173 |
174 | Default BASEDIR is /usr/local
175 |
176 | HOW TO INSTALL A MULTI-PLATFORM SET OF BINARIES
177 | 1. (Optional) Change BASEDIR, BINDIR and/or MP_DIR in Makefile
178 | 2. make install_multi_platform
179 | 3. (Optional) add links or binaries for your platform(s)
180 |
181 | HOME PAGE
182 | https://blogs.oracle.com/timc/entry/nicstat_the_solaris_and_linux
183 | ```
184 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | nicstat 1.95 README
2 | ===================
3 |
4 | nicstat is licensed under the Artistic License 2.0. You can find a
5 | copy of this license as LICENSE.txt included with the nicstat
6 | distribution, or at http://www.perlfoundation.org/artistic_license_2_0
7 |
8 |
9 | AUTHORS
10 | timothy.cook@oracle.com (formerly tim.cook@sun.com), Brendan Gregg
11 | (formerly Brendan.Gregg@sun.com)
12 |
13 | HOW TO BUILD ON SOLARIS
14 | mv Makefile.Solaris Makefile
15 | make
16 |
17 | HOW TO BUILD ON LINUX
18 | mv Makefile.Linux Makefile
19 | make
20 |
21 | HOW TO INSTALL
22 | make [BASEDIR=] install
23 |
24 | Default BASEDIR is /usr/local
25 |
26 | HOW TO INSTALL A MULTI-PLATFORM SET OF BINARIES
27 | 1. (Optional) Change BASEDIR, BINDIR and/or MP_DIR in Makefile
28 | 2. make install_multi_platform
29 | 3. (Optional) add links or binaries for your platform(s)
30 |
31 | HOME PAGE
32 | https://blogs.oracle.com/timc/entry/nicstat_the_solaris_and_linux
33 |
--------------------------------------------------------------------------------
/README_upstream.md:
--------------------------------------------------------------------------------
1 | nicstat is a Solaris and Linux command-line that prints out network
2 | statistics for all network interface cards (NICs), including packets,
3 | kilobytes per second, average packet sizes and more.
4 |
5 | It was developed by Tim Cook and Brendan Gregg, both formerly of Sun
6 | Microsystems.
7 |
8 | Changes for Version 1.95, January 2014
9 | --------------------------------------
10 |
11 | ## Common
12 |
13 | - Added "-U" option, to display separate read and write
14 | utilization.
15 |
16 | - Simplified display code regarding "-M" option.
17 |
18 | ## Solaris
19 |
20 | - Fixed fetch64() to check type of kstats
21 |
22 | - Fixed memory leak in update_nicdata_list()
23 |
24 | Changes for Version 1.92, October 2012
25 | --------------------------------------
26 |
27 | ## Common
28 |
29 | - Added "-M" option to change throughput statistics to Mbps
30 | (Megabits per second). Suggestion from Darren Todd.
31 |
32 | - Fixed bugs with printing extended parseable format (-xp)
33 |
34 | - Fixed man page's description of extended parseable output.
35 |
36 | ## Solaris
37 |
38 | - Fixed memory leak associated with g_getif_list
39 |
40 | - Add 2nd argument to dladm_open() for Solaris 11.1
41 |
42 | - Modify nicstat.sh to handle Solaris 11.1
43 |
44 | ## Linux
45 |
46 | - Modify nicstat.sh to see "x86_64" cputype as "i386". All Linux
47 | binaries are built as 32-bit, so we do not need to differentiate
48 | these two cpu types.
49 |
50 | Changes for Version 1.90, April 2011
51 | ------------------------------------
52 |
53 | ## Common
54 |
55 | - nicstat.sh script, to provide for automated multi-platform
56 | deployment. See the Makefile's for details.
57 |
58 | - Added "-x" flag, to display extended statistics for each
59 | interface.
60 |
61 | - Added "-t" and "-u" flags, to include TCP and UDP
62 | (respectively) statistics. These come from tcp:0:tcpstat
63 | and udp:0:udpstat on Solaris, or from /proc/net/snmp and
64 | /proc/net/netstat on Linux.
65 |
66 | - Added "-a" flag, which equates to "-tux".
67 |
68 | - Added "-l" flag, which lists interfaces and their
69 | configuration.
70 |
71 | - Added "-v" flag, which displays nicstat version.
72 |
73 | ## Solaris
74 |
75 | - Added use of libdladm.so:dladm_walk_datalink_id() to get list of
76 | interfaces. This is better than SIOCGLIFCONF, as it includes
77 | interfaces given exclusively to a zone.
78 | NOTE: this library/routine can be linked in to nicstat in "lazy"
79 | mode, meaning that a Solaris 11 binary built with knowledge of the
80 | routine will also run on Solaris 10 without failing when the routine
81 | or library is not found - in this case nicstat will fall back to the
82 | SIOGLIFCONF method.
83 |
84 | - Added search of kstat "link_state" statistics as a third
85 | method for finding active network interfaces. See the man
86 | page for details.
87 |
88 | ## Linux
89 |
90 | - Added support for SIOCETHTOOL ioctl, so that nicstat can
91 | look up interface speed/duplex (i.e. "-S" flag not necessarily
92 | needed any longer).
93 |
94 | - Removed need for LLONG_MAX, improving Linux portability.
95 |
--------------------------------------------------------------------------------
/dladm.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # dladm.sh - Build glue for libdladm:dladm_walk_datalink_id
3 |
4 | LIBDLADM=/lib/libdladm.so.1
5 |
6 | CPP_DEFINES="-DUSE_DLADM"
7 |
8 | if [ -f "$LIBDLADM" ]; then
9 | match=`nm -hgp $LIBDLADM | grep ' T dladm_walk_datalink_id'`
10 | if [ -n "$match" ]; then
11 | case $1 in
12 | 'def'* | -[dD] )
13 | if [ -f /usr/include/libnetcfg.h ]; then
14 | CPP_DEFINES="$CPP_DEFINES -DHAVE_LIBNETCFG"
15 | fi
16 | echo $CPP_DEFINES
17 | ;;
18 | 'lib'* | -l )
19 | echo "-zlazyload -ldladm" ;;
20 | esac
21 | fi
22 | fi
23 |
--------------------------------------------------------------------------------
/enicstat:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # enicstat - Get interface speed/duplex from ethtool; use with nicstat
3 | #
4 | # Copyright (c) 2009, Tim.Cook@sun.com
5 | #
6 | # nicstat is licensed under the Artistic License 2.0. You can find
7 | # a copy of this license as LICENSE.txt included with the nicstat
8 | # distribution, or at http://www.perlfoundation.org/artistic_license_2_0
9 |
10 | PROG=${0##*/}
11 |
12 | #-- Add /sbin to PATH, as that is where ethtool usually is
13 | export PATH=${PATH}:/sbin
14 |
15 | #-- Get list of interface names
16 | while read a b c
17 | do
18 | case $a in
19 | 'Inter'* | 'face' ) ;;
20 | *':'[0-9]* | *':' )
21 | ifname=${a%:*}
22 | interfaces="$interfaces $ifname"
23 | ;;
24 | esac
25 | done < /proc/net/dev
26 |
27 | #-- Get speed/duplex of each interface using ethtool
28 | nl='
29 | '
30 | for if in $interfaces
31 | do
32 | ifname=
33 | speed=
34 | duplex=
35 | valid=false
36 | settings=`ethtool "$if" 2>/dev/null`
37 | x=${settings#Settings for }
38 | ifname=${x%%:*}
39 | x=${settings##* Speed: }
40 | case $x in
41 | [1-9]*'Mb/s'* )
42 | valid=true ;;
43 | esac
44 | speed=${x%%Mb/s$nl*}
45 | x=${settings##* Duplex: }
46 | duplex=${x%%$nl*}
47 | case $valid,$ifspeeds in
48 | true, )
49 | ifspeeds="${ifname}:$speed$duplex" ;;
50 | true,* )
51 | ifspeeds="$ifspeeds,${ifname}:$speed$duplex" ;;
52 | esac
53 | done
54 |
55 | if [ -z "$ifspeeds" ]; then
56 | echo "$PROG: warning: failed to obtain any interface speeds" >&2
57 | exec nicstat "$@"
58 | fi
59 |
60 | #-- Run nicstat with what we got
61 | exec nicstat -S "$ifspeeds" "$@"
62 |
--------------------------------------------------------------------------------
/nicstat.1:
--------------------------------------------------------------------------------
1 | .\" nicstat.1 - Man page for nicstat(1)
2 | .\"
3 | .\" Copyright (c) 2009-2014, Tim Cook, Oracle Corporation
4 |
5 | .\" nicstat is licensed under the Artistic License 2.0. You can find a
6 | .\" copy of this license as LICENSE.txt included with the nicstat
7 | .\" distribution, or at http://www.perlfoundation.org/artistic_license_2_0
8 | .\"
9 | .\" @(#)nicstat.1 1.4 11/04/25 timothy.cook@oracle.com
10 | .\"
11 | .TH nicstat 1 "27 Jan 2014"
12 | .UC 4
13 | .\" ========================================================================
14 |
15 | .SH NAME
16 |
17 | nicstat, enicstat \- print network traffic statistics
18 | .\" ========================================================================
19 | .SH SYNOPSIS
20 | .B nicstat
21 | [-hvnsxpztualkMU]
22 | .RI [-i interface]
23 | .RI [-S int:mbps[fd|hd]]
24 | .I [interval
25 | .I [count]]
26 | .PP
27 | .B enicstat
28 | \fI
29 | .\" ========================================================================
30 |
31 | .SH DESCRIPTION
32 |
33 | .I nicstat
34 | prints out network statistics for all network cards (NICs),
35 | including packets, kilobytes per second, average packet sizes and more.
36 | .\" ========================================================================
37 |
38 | .SH OPTIONS
39 |
40 | .TP 1i
41 | .B \-h
42 | Display brief usage information (help).
43 | .TP 1i
44 | .B \-v
45 | Display nicstat version (and additional fields when combined with '-l')
46 | .TP 1i
47 | .B \-n
48 | Show statistics for non-local (i.e. non-loopback) interfaces only.
49 | .TP 1i
50 | .B \-s
51 | Display summary output - just the amount of data received (read) and
52 | transmitted (written).
53 | .TP 1i
54 | .B \-x
55 | Display extended output. See \fBOUTPUT\fR section for details.
56 | .TP 1i
57 | .B \-U
58 | Display separate read and write utilization statistics. This affects
59 | the default, extended (-x) and all (-a) format outputs. For the
60 | default format the "Sat" statistic is dropped to fit the output in 80
61 | columns.
62 | .TP 1i
63 | .B \-M
64 | Display interface throughput statistics in Mbps (megabits per second),
65 | instead of the default KB/s (kilobytes per second).
66 |
67 | NOTE - interface statistics are reported to operating systems in bytes.
68 | nicstat does not know if Ethernet or other hardware overheads are
69 | included in the statistic on each platform.
70 | .TP 1i
71 | .B \-p
72 | Display output in parseable format. This outputs one line per
73 | interface, in the following formats (which correspond to the
74 | default, -x, -t and -u options; respectively):
75 | .TP 1i
76 | .PP
77 | .I time:In:rKB/s:wKB/s:rPk/s:wPk/s:%Util:Sat
78 | .I time:In:rKB/s:wKB/s:rPk/s:wPk/s:%Util:Sat:IErr:OErr:Coll:NoCP:Defer
79 | .I time:\fRTCP\fI:InKB:OutKB:InSeg:OutSeg:Reset:AttF:%ReTX:InConn:OutCon:Drops
80 | .I time:\fRUDP\fI:InDG:OutDG:InErr:OutErr
81 | .TP 1i
82 | .B \
83 | where \fItime\fR is the number of seconds since midnight,
84 | Jan 1 1970 (UST) and the other fields are as described in the
85 | \fBOUTPUT\fR section below.
86 |
87 | NOTE - throughput statistics are always in KB/s (kilbytes per second)
88 | for parseable formats, even if the "-M" flag has been specified.
89 | .TP 1i
90 | .B \-z
91 | Skip interfaces for which there was zero traffic for the sample period.
92 | .TP 1i
93 | .B \-t
94 | Show TCP statistics.
95 | .TP 1i
96 | .B \-u
97 | Show UDP statistics.
98 | .TP 1i
99 | .B \-a
100 | Equvalent to '-x -t -u'.
101 | .TP 1i
102 | .B \-l
103 | Just list interfaces.
104 | .TP 1i
105 | .BI \-i interface[,interface ...]
106 | Show statistics for only the interface(s) listed. Multiple interfaces
107 | can be listed, separated by commas (,).
108 | .TP 1i
109 | .BI \-S int:speed[fd|hd]
110 | (Linux only).
111 | Specify the speed (and optionally duplex mode) of one or more interfaces.
112 | The given speed(s) are in megabits/second.
113 | The duplex mode will default to "full" unless a suffix beginning with
114 | "h" or "H" is specified.
115 | Speed and duplex mode are obtained automatically on Solaris using the
116 | "ifspeed" and "link_duplex" kstat values.
117 | .TP 1i
118 | .B \-k
119 | (Solaris only).
120 | Search for active network interfaces by looking for kstat "link_state"
121 | statistics with a value of 1. This is only of value on systems
122 | running Solaris 10 (or early releases of Solaris 11 Express), with
123 | Exclusive IP Zones, where the interfaces given to an Exclusive IP Zone
124 | are not otherwise visible. If you are running Solaris 9 (or earlier),
125 | or Solaris 11 (or later) you do not need this option.
126 | .\" ========================================================================
127 |
128 | .SH OPERANDS
129 |
130 | .TP 1i
131 | .I interval
132 | Specifies the number of seconds between samples.
133 |
134 | .TP 1i
135 | .I count
136 | Specifies the number of times that the statistics are repeated. If no
137 | .I count
138 | is specified,
139 | .B nicstat
140 | will repeat statistics indefinitely.
141 | .\" ========================================================================
142 |
143 | .SH OUTPUT
144 |
145 | The fields of \fBnicstat\fP's display are:
146 | .TP 1i
147 | .B Time
148 | The time corresponding to the end of the sample shown, in
149 | .I HH:MM:SS
150 | format (24-hour clock).
151 | .TP 1i
152 | .B Int
153 | The interface name.
154 | .TP 1i
155 | .B rKB/s, InKB
156 | Kilobytes/second read (received).
157 | .TP 1i
158 | .B wKB/s, OutKB
159 | Kilobytes/second written (transmitted).
160 | .TP 1i
161 | .B rMbps, RdMbps
162 | Megabits/second read (received).
163 | .TP 1i
164 | .B wMbps, WrMbps
165 | Megabits/second written (transmitted).
166 | .TP 1i
167 | .B rPk/s, InSeg, InDG
168 | Packets (TCP Segments, UDP Datagrams)/second read (received).
169 | .TP 1i
170 | .B wPk/s, OutSeg, OutDG
171 | Packets (TCP Segments, UDP Datagrams)/second written (transmitted).
172 | .TP 1i
173 | .B rAvs
174 | Average size of packets read (received).
175 | .TP 1i
176 | .B wAvs
177 | Average size of packets written (transmitted).
178 | .TP 1i
179 | .B %Util
180 | Percentage utilization of the interface. For full-duplex interfaces,
181 | this is the greater of
182 | .I rKB/s
183 | or
184 | .I wKB/s
185 | as a percentage of the interface speed.
186 | For half-duplex interfaces,
187 | .I rKB/s
188 | and
189 | .I wKB/s
190 | are summed.
191 | .TP 1i
192 | .B %rUtil, %wUtil
193 | Percentage utilization for bytes read and written, respectively.
194 | .TP 1i
195 | .B Sat
196 | Saturation. This the number of errors/second seen for the interface -
197 | an indicator the interface may be approaching saturation. This
198 | statistic is combined from a number of kernel statistics. It is
199 | recommended to use the '-x' option to see more individual statistics
200 | (those mentioned below) when attempting to diagnose a network issue.
201 |
202 | .TP 1i
203 | .B IErr
204 | Packets received that could not be processed because they contained
205 | errors
206 | .TP 1i
207 | .B OErr
208 | Packets that were not successfully transmitted because of errors
209 | .TP 1i
210 | .B Coll
211 | Ethernet collisions during transmit.
212 | .TP 1i
213 | .B NoCP
214 | No-can-puts. This is when an incoming packet can not be put to the
215 | process reading the socket. This suggests the local process is unable
216 | to process incoming packets in a timely manner.
217 | .TP 1i
218 | .B Defer
219 | Defer Transmits. Packets without collisions where first transmit
220 | attempt was delayed because the medium was busy.
221 |
222 | .TP 1i
223 | .B Reset
224 | tcpEstabResets. The number of times TCP connections have made a direct
225 | transition to the CLOSED state from either the ESTABLISHED state or
226 | the CLOSE-WAIT state.
227 | .TP 1i
228 | .B AttF
229 | tcpAttemptFails - The number of times that TCP connections have made a
230 | direct transition to the CLOSED state from either the SYN-SENT state
231 | or the SYN-RCVD state, plus the number of times TCP connections have
232 | made a direct transition to the LISTEN state from the SYN-RCVD state.
233 | .TP 1i
234 | .B %ReTX
235 | Percentage of TCP segments retransmitted - that is, the number of TCP
236 | segments transmitted containing one or more previously transmitted
237 | octets.
238 | .TP 1i
239 | .B InConn
240 | tcpPassiveOpens - The number of times that TCP connections have made a
241 | direct transition to the SYN-RCVD state from the LISTEN
242 | state.
243 | .TP 1i
244 | .B OutCon
245 | tcpActiveOpens - The number of times that TCP connections have made a
246 | direct transition to the SYN-SENT state from the CLOSED state.
247 | .TP 1i
248 | .B Drops
249 | tcpHalfOpenDrop + tcpListenDrop + tcpListenDropQ0.
250 | .PP
251 | \fItcpListenDrop\fR and \fItcpListenDropQ0\fR - Number of connections
252 | dropped from the completed connection queue and incomplete connection
253 | queue, respectively. \fItcpHalfOpenDrops\fR - Number of connections dropped
254 | after the initial SYN packet was received.
255 | .PP
256 | The first set of statistics printed are averages since system boot.
257 | If no
258 | .B interval
259 | operand is specified, or a
260 | .B count
261 | value of "1" is specified, this will be the only sample printed.
262 | .\" ========================================================================
263 | .SH EXAMPLES
264 | Print average statistics from boot time to now only:
265 | .PP
266 | .nf
267 | $ \fBnicstat
268 | .fi
269 | .PP
270 | Print statistics for all interfaces, every 3 seconds:
271 | .PP
272 | .nf
273 | $ \fBnicstat 3
274 | .fi
275 | .PP
276 | Print statistics for all interfaces, every 5 seconds, finishing after
277 | 10 samples:
278 | .PP
279 | .nf
280 | $ \fBnicstat 5 10
281 | .fi
282 | .PP
283 | Print statistics every 3 seconds, only for interfaces "hme0" and "hme1":
284 | .PP
285 | .nf
286 | $ \fBnicstat -i hme0,hme1 3
287 | .fi
288 | .PP
289 | Print statistics for non-local interfaces, setting speed of "eth0" and
290 | "eth1" to 10mbps/half-duplex and 1000mbps/full-duplex, respectively:
291 | .PP
292 | .nf
293 | $ \fBnicstat -n -S eth0:10h,eth1:1000 5
294 | .fi
295 | .\" ========================================================================
296 | .SH SEE\ ALSO
297 | .BR netstat (1M)
298 | .BR kstat (1M),
299 | .BR kstat (3KSTAT),
300 | .BR mibiisa (1M),
301 | .BR ethtool (8)
302 |
303 | "nicstat - the Solaris and Linux Network Monitoring Tool You Did Not Know You Needed"
304 | .RI - http://blogs.oracle.com/timc/entry/nicstat_the_solaris_and_linux
305 | .\" ========================================================================
306 | .SH NOTES
307 | .PP
308 | On Linux, the NoCP, Defer, TCP InKB, and TCP OutKB statistics are
309 | always reported as zero.
310 | .PP
311 | The way that saturation is reported is a best effort, as there is no
312 | standardized naming to capture all errors related to an interface's
313 | inability to receive or transmit a packet. Monitoring %Util and
314 | packet rates, along with an understanding of the specific NICs may be
315 | more useful in judging whether you are nearing saturation.
316 | .PP
317 | The
318 | .B \-S
319 | option is provided for the Linux edition as nicstat requires
320 | super-user privilege to obtain speed and duplex mode information for
321 | interfaces.
322 | If you are unable to set up nicstat as setuid-root, a script named
323 | .B enicstat
324 | is available, which uses the
325 | .B ethtool
326 | utility then calls nicstat with an
327 | .B \-S
328 | value.
329 | .B ethtool
330 | itself requires super-user privilege for this to work.
331 |
--------------------------------------------------------------------------------
/nicstat.c:
--------------------------------------------------------------------------------
1 | /*
2 | * nicstat - print network traffic, Kb/s read and written.
3 | *
4 | * Copyright (c) 2005-2014, Brendan.Gregg@sun.com and Tim.Cook@sun.com
5 | *
6 | * nicstat is licensed under the Artistic License 2.0. You can find
7 | * a copy of this license as LICENSE.txt included with the nicstat
8 | * distribution, or at http://www.perlfoundation.org/artistic_license_2_0
9 | */
10 |
11 | #define NICSTAT_VERSION "1.95"
12 |
13 | /* Is this GNU/Linux? */
14 | #if defined(__linux__) || defined(__linux) || defined(linux)
15 | #define OS_LINUX 1
16 | #endif
17 |
18 | /* Is this Solaris? */
19 | #if defined(sun) || defined(__sun)
20 | #if defined(__SVR4) || defined(__svr4__)
21 | #define OS_SOLARIS 1
22 | #endif
23 | #endif
24 |
25 | #if ! defined(OS_SOLARIS) && ! defined(OS_LINUX)
26 | #error "nicstat is not supported on your OS yet"
27 | #endif
28 |
29 | #ifndef DEBUG
30 | #define DEBUG 0
31 | #endif
32 |
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 | #include
45 | #include
46 | #include
47 | #include
48 |
49 | #ifdef OS_SOLARIS
50 | #include
51 | #include
52 | #include
53 | #ifdef USE_DLADM
54 | #include
55 | #include
56 | #include
57 | #include
58 | #ifdef HAVE_LIBNETCFG
59 | #include
60 | #endif
61 | #endif
62 | #ifndef LIFC_ALLZONES /* Comes from in 5.10 & later */
63 | #define LIFC_ALLZONES 0x08
64 | #endif
65 | #ifndef LIFC_UNDER_IPMP /* in 5.11 */
66 | #define LIFC_UNDER_IPMP 0x0
67 | #endif
68 | #ifndef LIFC_ENABLED /* in 5.11 */
69 | #define LIFC_ENABLED 0x0
70 | #endif
71 | #ifndef MAXLINKNAMELEN /* in 5.11 */
72 | #define MAXLINKNAMELEN LIFNAMSIZ
73 | #endif
74 | #define LIFR_FLAGS_TYPE uint64_t
75 | #else /* OS_SOLARIS */
76 | #include
77 | #endif /* OS_SOLARIS */
78 |
79 | #ifdef OS_LINUX
80 | /* #include */
81 | #include
82 | #include
83 | #include
84 | #include
85 | #define PROC_NET_DEV_PATH "/proc/net/dev"
86 | #define PROC_NET_SNMP_PATH "/proc/net/snmp"
87 | #define PROC_NET_NETSTAT_PATH "/proc/net/netstat"
88 | #define PROC_NET_BUFSIZ (128 * 1024)
89 | #define PROC_UPTIME "/proc/uptime"
90 | #define UINT32_MAX (4294967295U)
91 | /* Needs to be fixed if not built under ILP32 */
92 | typedef unsigned long long uint64_t;
93 | typedef unsigned int uint32_t;
94 | extern char *optarg;
95 | extern int optind, opterr, optopt;
96 | #endif /* OS_LINUX */
97 |
98 | #ifdef OS_LINUX
99 | typedef __u8 duplex_t;
100 | /* This may be defined by */
101 | #ifndef DUPLEX_UNKNOWN
102 | #define DUPLEX_UNKNOWN 0xFF
103 | #endif /* DUPLEX_UNKNOWN */
104 | #else
105 | typedef uint32_t duplex_t;
106 | #define DUPLEX_UNKNOWN 0
107 | #define DUPLEX_HALF 1
108 | #define DUPLEX_FULL 2
109 | #endif /* OS_LINUX */
110 |
111 | #ifndef B_TRUE
112 | #define B_TRUE 1
113 | #define B_FALSE 0
114 | #endif
115 |
116 | #ifndef streql
117 | #define streql(a, b) (strcmp((a), (b)) == 0)
118 | #endif
119 |
120 | #define PAGE_SIZE 20
121 | #define INTERVAL 1
122 | #define LOOP_MAX 1
123 |
124 | #ifdef OS_LINUX
125 | #define GETOPT_OPTIONS "hi:sS:znplvxtuaMmU"
126 | #else
127 | #define GETOPT_OPTIONS "hi:sznpklvxtuaMmU"
128 | #endif
129 |
130 | /*
131 | * UDP stats
132 | */
133 | typedef struct udp_stats {
134 | struct timeval tv; /* tv_sec, tv_usec */
135 | uint64_t inDatagrams;
136 | uint64_t outDatagrams;
137 | uint64_t inErrors;
138 | uint64_t outErrors;
139 | } udpstats_t;
140 |
141 | static udpstats_t *g_udp_old, *g_udp_new;
142 |
143 | typedef struct tcp_stats {
144 | struct timeval tv; /* tv_sec, tv_usec */
145 | uint64_t inDataInorderSegs;
146 | uint64_t outDataSegs;
147 | uint64_t inDataInorderBytes;
148 | uint64_t inDataUnorderSegs;
149 | uint64_t inDataUnorderBytes;
150 | uint64_t outDataBytes;
151 | uint64_t estabResets;
152 | uint64_t outRsts;
153 | uint64_t attemptFails;
154 | uint64_t retransBytes;
155 | uint64_t passiveOpens;
156 | uint64_t activeOpens;
157 | uint64_t halfOpenDrop;
158 | uint64_t listenDrop;
159 | uint64_t listenDropQ0;
160 | } tcpstats_t;
161 |
162 | static tcpstats_t *g_tcp_old, *g_tcp_new;
163 |
164 | #ifdef OS_SOLARIS
165 | static kstat_t *g_tcp_ksp, *g_udp_ksp;
166 | #endif
167 |
168 | /*
169 | * Interface stats
170 | */
171 | typedef struct nic_stats {
172 | struct timeval tv; /* tv_sec, tv_usec */
173 | uint64_t rbytes; /* total read bytes */
174 | uint64_t wbytes; /* total written bytes */
175 | uint64_t rpackets; /* total read packets */
176 | uint64_t wpackets; /* total written packets */
177 | uint64_t ierr; /* total input errors */
178 | uint64_t oerr; /* total output errors */
179 | uint64_t coll; /* total collisions */
180 | uint64_t nocp; /* total nocanput */
181 | uint64_t defer; /* total defers */
182 | uint64_t sat; /* saturation value */
183 | } nicstats_t;
184 |
185 | typedef struct nicdata {
186 | struct nicdata *next; /* pointer to next */
187 | char *name; /* interface name (e.g. "lo0") */
188 | uint32_t flags;
189 | #ifdef OS_LINUX
190 | int report; /* non-zero means we intend to print */
191 | #endif
192 | #ifdef OS_SOLARIS
193 | kstat_t *ls_ksp;
194 | kstat_t *op_ksp;
195 | uint32_t ls_types;
196 | uint32_t op_types;
197 | LIFR_FLAGS_TYPE if_flags;
198 | #endif
199 | uint64_t speed; /* speed of interface */
200 | duplex_t duplex;
201 | struct nic_stats old; /* stats from previous lookup */
202 | struct nic_stats new; /* stats from current lookup */
203 | } nicdata_t;
204 |
205 | typedef struct if_list {
206 | struct if_list *next;
207 | char *name;
208 | #ifdef OS_LINUX
209 | struct nicdata *nicp;
210 | #endif
211 | } if_list_t;
212 |
213 | /*
214 | * kstat type flags
215 | *
216 | * These are in decreasing order of preference; i.e, the highest order
217 | * bit will be chosen as the preferred source. These bits have been
218 | * chosen to allow addition above, below and in-between the existing
219 | * choices.
220 | */
221 | #define KS_LINK 0x40000 /* link::: */
222 | #define KS_DRV_MAC 0x10000 /* ::mac: */
223 | #define KS_DIN 0x04000 /* ::: */
224 | #define KS_DRV 0x01000 /* ::*: */
225 | #define KS_NAME 0x00400 /* :*:*: */
226 |
227 | /*
228 | * Other interface flags - for nicdata_t.flags
229 | */
230 | #define NIC_LIF_UP 0x00000001 /* IFF_UP */
231 | #define NIC_KS_UP 0x00000002 /* kstat link_state = 1 */
232 | #define NIC_LOOPBACK 0x00000010 /* Is a IFF_LOOPBACK */
233 | #define NIC_NO_GLIFFLAGS 0x00000100 /* no ioctl(,SIOCGLIFFLAGS,) */
234 | #define NIC_NO_KSTATS 0x00000200 /* Can't even get packets */
235 | #define NIC_NO_LINKSTATE 0x00000400 /* No :::link_state */
236 | #define NIC_NO_GSET 0x00000800 /* ETHTOOL_GSET fails */
237 | #define NIC_NO_SFLAG 0x00000200 /* No -S for this i'face */
238 | #define NIC_UP (NIC_KS_UP | NIC_LIF_UP)
239 |
240 | #define NIC_LK_IS_OK 0x00001000 /* ls_ksp == op_ksp */
241 |
242 | #define NIC_LK_UPDATED 0x00010000 /* ls_ksp up to date */
243 | #define NIC_OK_UPDATED 0x00020000 /* op_ksp up to date */
244 | #define NIC_KU_UPDATED 0x00040000 /* NIC_KS_UP up to date */
245 | #define NIC_LU_UPDATED 0x00080000 /* NIC_LIF_UP up to date */
246 |
247 | /* These bits indicate we have updated some data */
248 | #define NIC_UPDATED_FLAGS (NIC_LK_UPDATED | NIC_OK_UPDATED | \
249 | NIC_KU_UPDATED | NIC_LU_UPDATED)
250 |
251 | /* These bits are capabilities - should be static */
252 | #define NIC_CAPAB (NIC_CAN_GLIFFLAGS | NIC_HAVE_KSTATS | NIC_LOOPBACK)
253 |
254 | #ifdef OS_LINUX
255 | struct if_speed_list {
256 | struct if_speed_list *next;
257 | char *name;
258 | uint64_t speed;
259 | int duplex;
260 | };
261 | static struct if_speed_list *g_if_speed_list = NULL;
262 | #endif /* OS_LINUX */
263 |
264 | /*
265 | * This will contain everything we need to know about each interface, and
266 | * will be dynamically allocated.
267 | */
268 | static struct nicdata *g_nicdatap = NULL;
269 |
270 | /* Print style for NICs */
271 | enum { STYLE_FULL = 0, STYLE_FULL_UTIL, STYLE_SUMMARY, STYLE_PARSEABLE,
272 | STYLE_EXTENDED, STYLE_EXTENDED_UTIL,
273 | STYLE_EXTENDED_PARSEABLE, STYLE_NONE };
274 |
275 | static int g_nicdata_count = 0; /* number of if's we are tracking */
276 | static int g_style; /* output style */
277 | static int g_skipzero; /* skip zero value lines */
278 | static int g_nonlocal; /* list only non-local (exclude lo0) */
279 | static int g_someif; /* trace some interfaces only */
280 | static int g_list;
281 | static int g_udp; /* show UDP stats */
282 | static int g_tcp; /* show TCP stats */
283 | static int g_opt_x;
284 | static int g_opt_p;
285 | static int g_verbose;
286 | static int g_forever; /* run forever */
287 | static char **g_tracked; /* tracked interfaces */
288 | static int g_line; /* output line counter */
289 | static char *g_progname; /* ptr to argv[0] */
290 | static int g_caught_cont; /* caught SIGCONT - were suspended */
291 | static int g_opt_m; /* show results in Mbps (megabits) */
292 | static int g_opt_U; /* show in and out %Util */
293 |
294 | /* Used in display headers - default is when displaying KB/s */
295 | static char *g_runit_1 = "rKB/s";
296 | static char *g_wunit_1 = "wKB/s";
297 | static char *g_runit_2 = "RdKB";
298 | static char *g_wunit_2 = "WrKB";
299 |
300 | static int g_sock; /* Socket for interface ioctl's */
301 |
302 | #ifdef OS_SOLARIS
303 | static int g_opt_k;
304 | static kstat_ctl_t *g_kc; /* kstat chain pointer */
305 | static int g_new_kstat_chain = B_TRUE; /* kstat chain updated */
306 | #ifdef USE_DLADM
307 | /* This is set to TRUE if we can load the libdladm function we need */
308 | static int g_use_dladm;
309 | static dladm_handle_t g_handle = NULL;
310 | #endif
311 | #endif /* OS_SOLARIS */
312 |
313 | #ifdef OS_LINUX
314 | static unsigned long g_boot_time; /* when we booted */
315 | static FILE *g_snmp = NULL;
316 | static FILE *g_netstat = NULL;
317 | #endif /* OS_LINUX */
318 |
319 | /*
320 | * diag - print stderr message.
321 | *
322 | * This subroutine prints an error message, possibly including the meaning
323 | * of errno.
324 | */
325 | static void
326 | diag(int use_errno, char *format, ...)
327 | {
328 | va_list ap;
329 | char *error_str;
330 |
331 | (void) fprintf(stderr, "%s: ", g_progname);
332 | if (use_errno) {
333 | error_str = strerror(errno);
334 | if (! error_str)
335 | error_str = strerror(0);
336 | }
337 | va_start(ap, format);
338 | (void) vfprintf(stderr, format, ap);
339 | va_end(ap);
340 | if (use_errno)
341 | (void) fprintf(stderr, ": %s\n", error_str);
342 | else
343 | (void) fputc('\n', stderr);
344 | }
345 |
346 | #define die(...) \
347 | do { \
348 | diag(__VA_ARGS__); \
349 | exit(2); \
350 | } while (0)
351 |
352 | /*
353 | * usage - print a usage message and exit.
354 | */
355 | static void
356 | usage(void)
357 | {
358 | (void) fprintf(stderr,
359 | "USAGE: nicstat [-hvnsxpztualMU] [-i int[,int...]]\n "
360 | #ifdef OS_LINUX
361 | "[-S int:mbps[,int:mbps...]] "
362 | #endif
363 | "[interval [count]]\n"
364 | "\n"
365 | " -h # help\n"
366 | " -v # show version (" NICSTAT_VERSION ")\n"
367 | " -i interface # track interface only\n"
368 | " -n # show non-local interfaces only"
369 | " (exclude lo0)\n"
370 | " -s # summary output\n"
371 | " -x # extended output\n"
372 | " -p # parseable output\n"
373 | " -z # skip zero value lines\n"
374 | " -t # show TCP statistics\n"
375 | " -u # show UDP statistics\n"
376 | " -a # equivalent to \"-x -u -t\"\n"
377 | " -l # list interface(s)\n"
378 | " -M # output in Mbits/sec\n"
379 | " -U # separate %%rUtil and %%wUtil\n"
380 | #ifdef OS_LINUX
381 | " -S int:mbps[fd|hd] # tell nicstat the interface\n"
382 | " # speed (Mbits/sec) and duplex\n"
383 | #endif
384 | " eg,\n");
385 | (void) fprintf(stderr,
386 | " nicstat # print summary since boot only\n"
387 | " nicstat 1 # print every 1 second\n"
388 | " nicstat 1 5 # print 5 times only\n"
389 | " nicstat -z 1 # print every 1 second, skip zero"
390 | " lines\n"
391 | " nicstat -i hme0 1 # print hme0 only every 1 second\n");
392 | exit(1);
393 | }
394 |
395 | /*
396 | * new_string - simply strdup(3), but terminate on failure
397 | */
398 | static char *
399 | new_string(char *s)
400 | {
401 | char *new;
402 |
403 | new = strdup(s);
404 | if (! new)
405 | die(1, "strdup", g_progname);
406 | return (new);
407 | }
408 |
409 | /*
410 | * allocate() - calloc(3) - for zeroing, plus error handling
411 | */
412 | static inline void *
413 | allocate(size_t bytes)
414 | {
415 | void *p;
416 |
417 | p = calloc(1, bytes);
418 | if (p == NULL)
419 | die(1, "calloc");
420 | return (p);
421 | }
422 |
423 | /*
424 | * Return floating difference in timevals
425 | */
426 | static double
427 | tv_diff(struct timeval *new, struct timeval *old)
428 | {
429 | double new_d, old_d;
430 |
431 | new_d = (double)new->tv_sec;
432 | new_d += new->tv_usec / 1000000.0;
433 | old_d = (double)old->tv_sec;
434 | old_d += old->tv_usec / 1000000.0;
435 | return (new_d - old_d);
436 | }
437 |
438 | /*
439 | * if_is_ignored - return true if interface is to be ignored
440 | */
441 | static int
442 | if_is_ignored(char *if_name)
443 | {
444 | char **p;
445 |
446 | if (! g_someif)
447 | return (B_FALSE);
448 | for (p = g_tracked; *p; p++)
449 | if (streql(if_name, *p))
450 | return (B_FALSE);
451 | return (B_TRUE);
452 | }
453 |
454 | #ifdef OS_SOLARIS
455 | /*
456 | * Check interface list to see if an interface is in it
457 | */
458 | static int
459 | interface_in_list(char *interface, nicdata_t *nicp)
460 | {
461 | while (nicp) {
462 | if (streql(interface, nicp->name))
463 | return (B_TRUE);
464 | nicp = nicp->next;
465 | }
466 | return (B_FALSE);
467 | }
468 | #endif /* OS_SOLARIS */
469 |
470 | #ifdef OS_SOLARIS
471 | /*
472 | * reclaim_nicdata - reclaim's a struct nicdata * from our global list
473 | *
474 | * Return a struct nicdata pointer; if it is found in the global list; and
475 | * also remove it from the list (we are in the process of re-building the
476 | * list). Modifies g_nicdatap.
477 | */
478 | static struct nicdata *
479 | reclaim_nicdata(char *if_name)
480 | {
481 | struct nicdata *matchp, *prevp;
482 |
483 | prevp = NULL;
484 | for (matchp = g_nicdatap; matchp; matchp = matchp->next) {
485 | if (streql(matchp->name, if_name)) {
486 | /* Got a match */
487 | if (prevp)
488 | /* Splice head of list to tail of list */
489 | prevp->next = matchp->next;
490 | else
491 | /* We are at the head */
492 | g_nicdatap = matchp->next;
493 | /* Disassociate match with the tail of the list */
494 | matchp->next = NULL;
495 | return (matchp);
496 | }
497 | prevp = matchp;
498 | }
499 | return (NULL);
500 | }
501 | #endif /* OS_SOLARIS */
502 |
503 | #ifdef OS_SOLARIS
504 | /*
505 | * fetch64 - return a uint64_t value from kstat.
506 | *
507 | * The arguments are a kstat pointer, the value name,
508 | * and a default value in case the lookup fails.
509 | */
510 | static uint64_t
511 | fetch64(kstat_t *ksp, char *value64, uint64_t def)
512 | {
513 | kstat_named_t *knp; /* Kstat named pointer */
514 |
515 | /* try a lookup and return */
516 | if ((knp = kstat_data_lookup(ksp, value64)) != NULL)
517 | /* Rely on C type conversion to promote smaller size values */
518 | switch (knp->data_type) {
519 | case KSTAT_DATA_INT32:
520 | return (knp->value.i32);
521 | /*NOTREACHED*/
522 | case KSTAT_DATA_UINT32:
523 | return (knp->value.ui32);
524 | /*NOTREACHED*/
525 | case KSTAT_DATA_INT64:
526 | return (knp->value.i64);
527 | /*NOTREACHED*/
528 | case KSTAT_DATA_UINT64:
529 | return (knp->value.ui64);
530 | /*NOTREACHED*/
531 | }
532 | return (def);
533 | }
534 | #endif /* OS_SOLARIS */
535 |
536 | #ifdef OS_SOLARIS
537 | /*
538 | * fetch32 - return a uint32_t value from kstat.
539 | *
540 | * The arguments are a kstat pointer, the value name,
541 | * and a default value in case the lookup fails.
542 | */
543 | static uint32_t
544 | fetch32(kstat_t *ksp, char *value, uint32_t def)
545 | {
546 | kstat_named_t *knp; /* Kstat named pointer */
547 |
548 | /* try a lookup and return */
549 | if ((knp = kstat_data_lookup(ksp, value)) != NULL)
550 | return (knp->value.ui32);
551 | return (def);
552 | }
553 | #endif /* OS_SOLARIS */
554 |
555 | #ifdef OS_SOLARIS
556 | /*
557 | * fetch6432 - return a uint64_t or a uint32_t value from kstat.
558 | *
559 | * The arguments are a kstat pointer, a potential ui64 value name,
560 | * a potential ui32 value name, and a default value in case both
561 | * lookup fails. The ui64 value is attempted first.
562 | */
563 | static uint64_t
564 | fetch6432(kstat_t *ksp, char *value64, char *value, uint64_t def)
565 | {
566 | kstat_named_t *knp; /* Kstat named pointer */
567 |
568 | /* try lookups and return */
569 | if ((knp = kstat_data_lookup(ksp, value64)) != NULL)
570 | return (knp->value.ui64);
571 | if ((knp = kstat_data_lookup(ksp, value)) != NULL)
572 | return (knp->value.ui32);
573 | return (def);
574 | }
575 | #endif /* OS_SOLARIS */
576 |
577 | #ifdef OS_SOLARIS
578 | /*
579 | * fetch_nocanput - return nocanput value, whose name(s) are driver-dependent.
580 | *
581 | * Most drivers have a kstat "nocanput", but the ce driver
582 | * at least has "rx_nocanput" and "tx_nocanput"
583 | */
584 | static uint32_t
585 | fetch_nocanput(kstat_t *ksp, uint32_t def)
586 | {
587 | kstat_named_t *knp; /* Kstat named pointer */
588 | uint32_t sum;
589 |
590 | /* These should go in order of decreasing prevalence */
591 | if ((knp = kstat_data_lookup(ksp, "norcvbuf")) != NULL)
592 | return (knp->value.ui32);
593 | if ((knp = kstat_data_lookup(ksp, "nocanput")) != NULL)
594 | return (knp->value.ui32);
595 | if ((knp = kstat_data_lookup(ksp, "rx_nocanput")) != NULL) {
596 | sum = knp->value.ui32;
597 | if ((knp = kstat_data_lookup(ksp, "tx_nocanput"))
598 | != NULL) {
599 | sum += knp->value.ui32;
600 | return (sum);
601 | }
602 | }
603 | return (def);
604 | }
605 | #endif /* OS_SOLARIS */
606 |
607 | #ifdef OS_SOLARIS
608 | /*
609 | * fetch_boot_time - return the boot time in secs.
610 | *
611 | * This takes a kstat control pointer and looks up the boot time
612 | * from unix:0:system_misc:boot:time. If found, this is returned,
613 | * else 0.
614 | */
615 | static time_t
616 | fetch_boot_time()
617 | {
618 | kstat_t *ksp; /* Kstat struct pointer */
619 | kstat_named_t *knp; /* Kstat named pointer */
620 | static time_t boot_time = 0; /* Cache it if we can */
621 |
622 | if (boot_time != 0)
623 | return (boot_time);
624 | if ((ksp = kstat_lookup(g_kc, "unix", 0, "system_misc")) == NULL)
625 | die(1, "kstat_lookup: unix:0:system_misc");
626 | if ((kstat_read(g_kc, ksp, NULL) != -1) &&
627 | ((knp = kstat_data_lookup(ksp, "boot_time")) != NULL))
628 | /* summary since boot */
629 | boot_time = knp->value.ui32;
630 | /* This will be zero if kstat_data_lookup() failed */
631 | return (boot_time);
632 | }
633 | #endif /* OS_SOLARIS */
634 |
635 | #ifdef OS_LINUX
636 | /*
637 | * fetch_boot_time - return the boot time in secs.
638 | *
639 | * Gets the boot time from /proc.
640 | */
641 | static unsigned long
642 | fetch_boot_time()
643 | {
644 | char buf[64];
645 | int uptime_fd, bufsiz, scanned;
646 | unsigned long uptime;
647 |
648 | uptime_fd = open(PROC_UPTIME, O_RDONLY, 0);
649 | if (uptime_fd < 0)
650 | die(1, "error opening %s for read", PROC_UPTIME);
651 | bufsiz = read(uptime_fd, buf, sizeof (buf) - 1);
652 | if (bufsiz < 0)
653 | die(1, "read: %s", PROC_UPTIME);
654 | buf[bufsiz] = '\0';
655 | scanned = sscanf(buf, "%lu.", &uptime);
656 | if (scanned != 1)
657 | die(0, "cannot get uptime from %s", PROC_UPTIME);
658 | return (time(0) - uptime);
659 | }
660 | #endif /* OS_LINUX */
661 |
662 | #ifdef OS_SOLARIS
663 | static if_list_t *g_getif_list = NULL; /* Used by the lifc & dladm routines */
664 |
665 | static if_list_t *
666 | get_if_list_lifc(if_list_t *p)
667 | {
668 | if_list_t *newp, *headp;
669 | struct lifnum if_num; /* Includes # of if's */
670 | struct lifconf if_conf; /* Includes ptr to list of names */
671 |
672 | static struct ifreq *current_lif = (struct ifreq *)NULL;
673 | struct lifreq *if_reqp, req;
674 | int lif_size, lif_count, i;
675 |
676 | headp = p;
677 |
678 | /* Get number of interfaces on system */
679 | if_num.lifn_family = AF_UNSPEC;
680 | if_num.lifn_flags = LIFC_NOXMIT | LIFC_ALLZONES | LIFC_UNDER_IPMP
681 | | LIFC_ENABLED;
682 | if (ioctl(g_sock, SIOCGLIFNUM, &if_num) < 0)
683 | die(1, "ioctl(IFNUM)");
684 |
685 | /* Allocate my struct ifreq array buffer */
686 | lif_size = (if_num.lifn_count + 1) * sizeof (struct lifreq);
687 | current_lif = realloc(current_lif, lif_size);
688 | if (! current_lif)
689 | die(1, "realloc");
690 |
691 | /* Get the current interface list via the ioctl() */
692 | if_conf.lifc_family = AF_UNSPEC;
693 | if_conf.lifc_flags = if_num.lifn_flags;
694 | if_conf.lifc_len = lif_size;
695 | if_conf.lifc_buf = (caddr_t)current_lif;
696 | if (ioctl(g_sock, SIOCGLIFCONF, &if_conf) < 0)
697 | die(1, "ioctl(IFCONF)");
698 | lif_size = if_conf.lifc_len;
699 | lif_count = if_conf.lifc_len / sizeof (struct lifreq);
700 |
701 | /*
702 | * Loop through entries in lifc_req, making a list of interfaces
703 | */
704 | if_reqp = if_conf.lifc_req;
705 | (void) memset((void *) &req, 0, sizeof (struct lifreq));
706 | for (i = lif_count; i; i--, if_reqp++) {
707 | /* Skip virtual IP's */
708 | if (strchr(if_reqp->lifr_name, ':'))
709 | continue;
710 | (void) strlcpy(req.lifr_name, if_reqp->lifr_name, LIFNAMSIZ);
711 |
712 | /*
713 | * Skip interface if "-i" was used, and it is not
714 | * a matching interface
715 | */
716 | if (if_is_ignored(if_reqp->lifr_name))
717 | continue;
718 |
719 | /* Add to list */
720 | if (p->name) {
721 | /* Need new tail */
722 | newp = allocate(sizeof (if_list_t));
723 | p->next = newp;
724 | p = newp;
725 | }
726 | p->name = new_string(if_reqp->lifr_name);
727 | }
728 | return (headp);
729 | }
730 | #endif /* OS_SOLARIS */
731 |
732 | #ifdef USE_DLADM
733 | /*
734 | * dladm_callback - Function called by dladm_walk_datalink_id() for each
735 | * link.
736 | */
737 | /* ARGSUSED */
738 | static int
739 | dladm_callback(dladm_handle_t dh, datalink_id_t linkid, void *arg)
740 | {
741 | dladm_status_t status;
742 | char link[MAXLINKNAMELEN];
743 | datalink_class_t class;
744 | uint_t mtu;
745 | uint32_t flags;
746 | struct if_list *p, *newp;
747 |
748 | if ((status = dladm_datalink_id2info(g_handle, linkid, &flags, &class,
749 | NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
750 | return (status);
751 | }
752 |
753 | /*
754 | * Skip interface if "-i" was used, and it is not
755 | * a matching interface
756 | */
757 | if (if_is_ignored(link))
758 | return (DLADM_WALK_CONTINUE);
759 |
760 | p = g_getif_list;
761 | if (p->name) {
762 | /* Need new tail */
763 | newp = allocate(sizeof (struct if_list));
764 | p->next = newp;
765 | p = newp;
766 | g_getif_list = newp;
767 | }
768 | p->name = new_string(link);
769 | return (DLADM_WALK_CONTINUE);
770 | }
771 |
772 | /*
773 | * Get the current list of interfaces
774 | */
775 | static struct if_list *
776 | get_if_list_dl(if_list_t *p)
777 | {
778 | uint32_t flags = DLADM_OPT_ACTIVE;
779 |
780 | /* Start with "lo0" unless it is ignored */
781 | if (! g_nonlocal && (! if_is_ignored("lo0"))) {
782 | p->name = new_string("lo0");
783 | p->next = (struct if_list *)NULL;
784 | }
785 |
786 | /* dladm_callback() will append entries to g_getif_list */
787 | g_getif_list = p;
788 | (void) dladm_walk_datalink_id(dladm_callback, g_handle,
789 | NULL, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
790 | flags);
791 |
792 | return (g_getif_list = p);
793 | }
794 | #endif /* USE_DLADM */
795 |
796 | #ifdef OS_SOLARIS
797 | /*
798 | * Get the list from kstats, looking for one of:
799 | *
800 | * Class 4-tuple
801 | * ===== =======
802 | * net link:::link_state
803 | * net ::mac:link_state
804 | * mac ::/xx:link_state
805 | *
806 | * with a value of "1".
807 | *
808 | * This is only useful on S10 or newer; where interfaces given
809 | * exclusively to non-global zones may not be visible via
810 | * get_if_list_lifc(); and where USE_DLADM is not available.
811 | */
812 | static if_list_t *
813 | get_if_list_kstat(if_list_t *p)
814 | {
815 | if_list_t *newp, *headp;
816 | kstat_t *ksp;
817 | kstat_named_t *knp;
818 | char ifname[MAXLINKNAMELEN];
819 | char *namep;
820 |
821 | headp = p;
822 |
823 | /* Start with "lo0" unless it is ignored */
824 | if (! g_nonlocal && (! if_is_ignored("lo0"))) {
825 | p->name = new_string("lo0");
826 | p->next = (struct if_list *)NULL;
827 | }
828 |
829 | for (ksp = g_kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
830 | if (ksp->ks_type != KSTAT_TYPE_NAMED)
831 | continue;
832 | if (streql(ksp->ks_class, "net")) {
833 | if (streql(ksp->ks_module, "link")) {
834 | namep = ksp->ks_name;
835 | goto lookup;
836 | }
837 | if (streql(ksp->ks_name, "mac")) {
838 | (void) sprintf(ifname, "%s%u", ksp->ks_module,
839 | ksp->ks_instance);
840 | namep = ifname;
841 | goto lookup;
842 | }
843 | continue;
844 | }
845 | if (streql(ksp->ks_class, "mac")) {
846 | namep = ksp->ks_module;
847 | goto lookup;
848 | }
849 | continue;
850 |
851 | lookup:
852 | if (kstat_read(g_kc, ksp, NULL) < 0)
853 | die(1, "kstat_read");
854 | if (! (knp = kstat_data_lookup(ksp, "link_state")))
855 | continue;
856 | /* We have a "link_state" */
857 | if (knp->data_type != KSTAT_DATA_UINT32)
858 | continue;
859 | if (knp->value.ui32 != 1)
860 | continue;
861 |
862 | /* We have a value of 1 - link is UP */
863 | if (if_is_ignored(ifname))
864 | continue;
865 |
866 | /* Add to list */
867 | if (p->name) {
868 | /* Need new tail */
869 | newp = allocate(sizeof (if_list_t));
870 | p->next = newp;
871 | p = newp;
872 | }
873 | p->name = new_string(namep);
874 | }
875 | return (headp);
876 | }
877 | #endif /* OS_SOLARIS */
878 |
879 | #ifdef OS_SOLARIS
880 | static if_list_t *
881 | get_if_list()
882 | {
883 | /* Free g_getif_list if needed */
884 | if (g_getif_list) {
885 | struct if_list *p, *next;
886 |
887 | for (p = g_getif_list; p; ) {
888 | next = p->next;
889 | if (p->name)
890 | free(p->name);
891 | free(p);
892 | p = next;
893 | }
894 | }
895 |
896 | /* Allocate new g_getif_list */
897 | g_getif_list = allocate(sizeof (if_list_t));
898 |
899 | if (g_opt_k)
900 | return (get_if_list_kstat(g_getif_list));
901 | #ifdef USE_DLADM
902 | if (g_use_dladm)
903 | return (get_if_list_dl(g_getif_list));
904 | #endif
905 | return (get_if_list_lifc(g_getif_list));
906 | }
907 | #endif /* OS_SOLARIS */
908 |
909 | #ifdef OS_SOLARIS
910 | static LIFR_FLAGS_TYPE
911 | get_lif_flags(char *if_name)
912 | {
913 | struct lifreq req;
914 |
915 | (void) strlcpy(req.lifr_name, if_name, LIFNAMSIZ);
916 | if (ioctl(g_sock, SIOCGLIFINDEX, &req) == -1) {
917 | return (0);
918 | }
919 | if (ioctl(g_sock, SIOCGLIFFLAGS, &req) == -1) {
920 | return (0);
921 | }
922 | return (req.lifr_flags);
923 | }
924 |
925 | /*
926 | * split_ifname()
927 | *
928 | * Splits interface names like "bge0", "e1000g7001" into driver/module name
929 | * and instance number. The instance number is the largest set of trailing
930 | * digits.
931 | */
932 | static int
933 | split_ifname(char *if_name, char *drv, uint32_t *instance)
934 | {
935 | char *p;
936 | int n, m;
937 |
938 | n = 0;
939 | for (p = if_name; *p; p++)
940 | n++;
941 | if (n <= 1)
942 | return (B_FALSE);
943 | m = n;
944 | for (p--; isdigit(*p); p--)
945 | n--;
946 | if (m == n || n == 0)
947 | return (B_FALSE);
948 | (void) strncpy(drv, if_name, n);
949 | drv[n] = '\0';
950 | *instance = (uint32_t)atol(++p);
951 | return (B_TRUE);
952 | }
953 |
954 | /*
955 | * OUTPUTS
956 | * nic->ls_ksp
957 | * nic->op_ksp
958 | * nic->flags (NIC_KS_UP bit)
959 | */
960 | static nicdata_t *
961 | discover_kstats(char *if_name, nicdata_t *nic)
962 | {
963 | uint32_t if_instance;
964 | kstat_t *ksp;
965 | kstat_named_t *knp;
966 | int ks_link_state; /* :::link_state */
967 | int ks_opackets; /* :::opackets */
968 | int ks_link_module; /* link::: */
969 | int ks_drv_module; /* ::: */
970 | int ks_ifname_module; /* ::: */
971 | uint32_t n;
972 | uint32_t ttype;
973 | char if_drv[MAXLINKNAMELEN];
974 |
975 | if (! split_ifname(if_name, if_drv, &if_instance))
976 | die(0, "%s: %s: invalid interface name\n", g_progname,
977 | if_name);
978 |
979 | nic->ls_ksp = NULL;
980 | nic->op_ksp = NULL;
981 | for (ksp = g_kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
982 | if (ksp->ks_type != KSTAT_TYPE_NAMED)
983 | continue;
984 | if (strcmp(ksp->ks_class, "net") != 0)
985 | continue;
986 |
987 | ks_link_module = ks_drv_module = ks_ifname_module = B_FALSE;
988 | if (streql(ksp->ks_module, "link"))
989 | ks_link_module = B_TRUE;
990 | else if (streql(ksp->ks_module, if_drv) &&
991 | (ksp->ks_instance == if_instance))
992 | ks_drv_module = B_TRUE;
993 | else if (streql(ksp->ks_module, if_name))
994 | ks_ifname_module = B_TRUE;
995 | else
996 | continue;
997 |
998 | /* We have [link:::], [:::] or [:::] */
999 |
1000 | (void) kstat_read(g_kc, ksp, NULL);
1001 | knp = KSTAT_NAMED_PTR(ksp);
1002 | for (n = 0; n < ksp->ks_ndata; n++, knp++) {
1003 | ks_link_state = B_FALSE;
1004 | ks_opackets = B_FALSE;
1005 | if (streql(knp->name, "link_state"))
1006 | ks_link_state = B_TRUE;
1007 | else if (streql(knp->name, "opackets"))
1008 | ks_opackets = B_TRUE;
1009 | else
1010 | continue;
1011 |
1012 | /* knp is one of our desired statistics */
1013 | if (ks_link_module) {
1014 | if (streql(ksp->ks_name, if_name)) {
1015 | ttype = KS_LINK;
1016 | if (ks_link_state)
1017 | goto set_ls_kstat;
1018 | else
1019 | goto set_op_kstat;
1020 | }
1021 | continue;
1022 | }
1023 |
1024 | if (ks_drv_module &&
1025 | (ksp->ks_instance == if_instance)) {
1026 | if (streql(ksp->ks_name, "mac")) {
1027 | ttype = KS_DRV_MAC;
1028 | if (ks_link_state)
1029 | goto set_ls_kstat;
1030 | else
1031 | goto set_op_kstat;
1032 | } else if (streql(ksp->ks_name, if_name)) {
1033 | ttype = KS_DIN;
1034 | if (ks_link_state)
1035 | goto set_ls_kstat;
1036 | else
1037 | goto set_op_kstat;
1038 | } else {
1039 | ttype = KS_DRV;
1040 | if (ks_link_state)
1041 | goto set_ls_kstat;
1042 | else
1043 | goto set_op_kstat;
1044 | }
1045 | }
1046 |
1047 | if (ks_ifname_module) {
1048 | ttype = KS_NAME;
1049 | if (ks_link_state)
1050 | goto set_ls_kstat;
1051 | else
1052 | goto set_op_kstat;
1053 | }
1054 |
1055 | set_ls_kstat:
1056 | if (ttype > nic->ls_types) {
1057 | nic->ls_ksp = ksp;
1058 | if (knp->value.ui32 == 1)
1059 | nic->flags |= NIC_KS_UP;
1060 | nic->flags |= (NIC_LK_UPDATED |
1061 | NIC_KU_UPDATED);
1062 | }
1063 | nic->ls_types |= ttype;
1064 | continue;
1065 |
1066 | set_op_kstat:
1067 | if (ttype > nic->op_types) {
1068 | nic->op_ksp = ksp;
1069 | nic->flags |= NIC_OK_UPDATED;
1070 | }
1071 | nic->op_types |= ttype;
1072 | continue;
1073 | }
1074 | }
1075 | if (nic->ls_ksp == NULL) {
1076 | nic->flags |= NIC_NO_LINKSTATE;
1077 | if (nic->op_ksp == NULL)
1078 | nic->flags |= NIC_NO_KSTATS;
1079 | } else if (nic->ls_ksp == nic->op_ksp)
1080 | nic->flags |= NIC_LK_IS_OK;
1081 | return (nic);
1082 | }
1083 |
1084 | static kstat_t *
1085 | fetch_ksp(char *module, uint32_t instance, char *name)
1086 | {
1087 | kstat_t *ksp;
1088 |
1089 | ksp = kstat_lookup(g_kc, module, instance, name);
1090 | if (! ksp)
1091 | die(1, "kstat_lookup (\"%s:%u:%s\")", module, instance, name);
1092 | if (kstat_read(g_kc, ksp, NULL) == -1)
1093 | die(1, "kstat_read (\"%s:%u:%s\")", module, instance, name);
1094 | return (ksp);
1095 | }
1096 |
1097 | static void
1098 | update_ksp_by_type(kstat_t **kspp, uint32_t types, char *name)
1099 | {
1100 | char drv[MAXLINKNAMELEN];
1101 | uint32_t instance;
1102 |
1103 | if (*kspp && (! g_new_kstat_chain))
1104 | return;
1105 |
1106 | /* Need to get new ksp */
1107 | if (types & KS_LINK) {
1108 | *kspp = fetch_ksp("link", 0, name);
1109 | return;
1110 | }
1111 | (void) split_ifname(name, drv, &instance);
1112 | if (types & KS_DRV_MAC) {
1113 | *kspp = fetch_ksp(drv, instance, "mac");
1114 | return;
1115 | }
1116 | if (types & KS_DIN) {
1117 | *kspp = fetch_ksp(drv, instance, name);
1118 | return;
1119 | }
1120 | if (types & KS_DRV) {
1121 | *kspp = fetch_ksp(drv, instance, NULL);
1122 | return;
1123 | }
1124 | if (types & KS_NAME) {
1125 | *kspp = fetch_ksp(name, -1, NULL);
1126 | return;
1127 | }
1128 | die(0, "types = 0x%08x", types);
1129 | }
1130 |
1131 | static void
1132 | update_linkstate(nicdata_t *nicp)
1133 | {
1134 | kstat_named_t *knp;
1135 |
1136 | knp = kstat_data_lookup(nicp->ls_ksp, "link_state");
1137 | if (! knp)
1138 | die(1, "kstat_data_lookup(\"link_state\")");
1139 | if (knp->value.ui32 == 1)
1140 | nicp->flags |= NIC_KS_UP;
1141 | else
1142 | nicp->flags &= ~NIC_KS_UP;
1143 | nicp->flags |= NIC_KU_UPDATED;
1144 | }
1145 |
1146 | /*
1147 | * update_nicdata_list - update global linked list of nic data
1148 | *
1149 | * get current list of nics
1150 | * foreach (current nic)
1151 | * reclaim nicdata from g_nicdatap
1152 | * if (ioctl available)
1153 | * update NIC_IF_UP
1154 | * else
1155 | * update NIC_KS_UP
1156 | * if (iface is up)
1157 | * if (iface is new || kstat chain updated)
1158 | * update kstat pointers
1159 | * add current nic to new list
1160 | * free any remaining on old list
1161 | */
1162 | static void
1163 | update_nicdata_list()
1164 | {
1165 | struct nicdata *nicp, *new_nicdatap, *old_nicdatap;
1166 | struct nicdata *new_headp, *new_tailp;
1167 | struct if_list *if_listp, *ifp;
1168 | LIFR_FLAGS_TYPE if_flags;
1169 | uint32_t new_nics;
1170 |
1171 | if_listp = get_if_list();
1172 |
1173 | new_headp = NULL;
1174 | new_tailp = NULL;
1175 | new_nics = 0;
1176 | /* Outer loop - if_listp */
1177 | for (ifp = if_listp; ifp && ifp->name; ifp = ifp->next) {
1178 | if (interface_in_list(ifp->name, new_headp))
1179 | /* Seen it */
1180 | continue;
1181 | nicp = reclaim_nicdata(ifp->name);
1182 | if (! nicp) {
1183 | /* Was not previously known */
1184 | nicp = allocate(sizeof (nicdata_t));
1185 | nicp->name = new_string(ifp->name);
1186 | if_flags = get_lif_flags(ifp->name);
1187 | if (if_flags == 0) {
1188 | nicp->flags |= NIC_NO_GLIFFLAGS;
1189 | } else {
1190 | if (if_flags & IFF_UP)
1191 | nicp->flags |= NIC_LIF_UP;
1192 | nicp->flags |= NIC_LU_UPDATED;
1193 | if (if_flags & IFF_LOOPBACK)
1194 | nicp->flags |= NIC_LOOPBACK;
1195 | }
1196 | if (! discover_kstats(ifp->name, nicp))
1197 | nicp->flags |= NIC_NO_KSTATS;
1198 | } else {
1199 | /* Assume state is now out of date */
1200 | nicp->flags &= ~(NIC_UPDATED_FLAGS);
1201 | }
1202 |
1203 | /* Add to new_nicdatap */
1204 | if (new_tailp)
1205 | new_tailp->next = nicp;
1206 | else
1207 | new_headp = nicp;
1208 | new_tailp = nicp;
1209 |
1210 | if (g_nonlocal && (nicp->flags & NIC_LOOPBACK))
1211 | continue;
1212 |
1213 | /* Update UP/DOWN */
1214 | if (nicp->flags & NIC_NO_GLIFFLAGS) {
1215 | if ((nicp->flags & NIC_NO_KSTATS) ||
1216 | (nicp->flags & NIC_NO_LINKSTATE))
1217 | /* We will never know */
1218 | continue;
1219 | else if (! (nicp->flags & NIC_LK_UPDATED)) {
1220 | update_ksp_by_type(&(nicp->ls_ksp),
1221 | nicp->ls_types, nicp->name);
1222 | update_linkstate(nicp);
1223 | nicp->flags |= (NIC_LK_UPDATED |
1224 | NIC_KU_UPDATED);
1225 | }
1226 | } else {
1227 | if (! (nicp->flags & NIC_LU_UPDATED)) {
1228 | if_flags = get_lif_flags(ifp->name);
1229 | if (if_flags & IFF_UP)
1230 | nicp->flags |= NIC_LIF_UP;
1231 | else
1232 | nicp->flags &= ~NIC_LIF_UP;
1233 | nicp->flags |= NIC_LU_UPDATED;
1234 | }
1235 | }
1236 | if (! (nicp->flags & NIC_UP))
1237 | /* IF is down */
1238 | if (! g_list)
1239 | continue;
1240 | new_nics++;
1241 | }
1242 | g_nicdata_count = new_nics;
1243 |
1244 | /* Clean up any left in the old list */
1245 | for (new_nicdatap = g_nicdatap; new_nicdatap; ) {
1246 | old_nicdatap = new_nicdatap;
1247 | new_nicdatap = new_nicdatap->next;
1248 | free(old_nicdatap->name);
1249 | free(old_nicdatap);
1250 | }
1251 |
1252 | /* Save the new list we just built in our global pointer */
1253 | g_nicdatap = new_headp;
1254 | }
1255 | #endif /* OS_SOLARIS */
1256 |
1257 | #ifdef OS_LINUX
1258 | /*
1259 | * find_nicdatap - find a struct nicdata * from linked list
1260 | *
1261 | * We search the linked list starting from *lastp (or *headp if *lastp
1262 | * is NULL). All entries are searched until either:
1263 | *
1264 | * - matching if_name found, and we return the struct pointer
1265 | *
1266 | * - no match, so we initialise a new struct, add to the end of
1267 | * the list (or after *lastp if non-null) and return a pointer to it
1268 | *
1269 | * SIDE EFFECT - *lastp is always set to a pointer to the
1270 | * matched (or newly-created) struct. This allows an efficient
1271 | * sequential update of the list.
1272 | */
1273 |
1274 | enum search_state {HEAD, LAST, LAST_LOOPED};
1275 |
1276 | static struct nicdata *
1277 | find_nicdatap(struct nicdata **headp, struct nicdata **lastp, char *if_name)
1278 | {
1279 | struct nicdata *prevp, *p;
1280 | enum search_state state;
1281 |
1282 | prevp = NULL;
1283 |
1284 | if (*lastp && (*lastp)->next) {
1285 | state = LAST;
1286 | p = (*lastp)->next;
1287 | } else {
1288 | state = HEAD;
1289 | p = *headp;
1290 | }
1291 | while (p) {
1292 | /* Check for a match */
1293 | if (streql(p->name, if_name)) {
1294 | /* We have a match */
1295 | *lastp = p;
1296 | return (p);
1297 | }
1298 | prevp = p;
1299 | p = p->next;
1300 | if (p == NULL) {
1301 | switch (state) {
1302 | case HEAD:
1303 | case LAST_LOOPED:
1304 | /* Will terminate loop */
1305 | break;
1306 | case LAST:
1307 | /* Start from head */
1308 | state = LAST_LOOPED;
1309 | p = *headp;
1310 | break;
1311 | }
1312 | } else
1313 | if (state == LAST_LOOPED &&
1314 | p == *lastp)
1315 | /* No match */
1316 | break;
1317 | }
1318 |
1319 | /* We get here if we have no match */
1320 | p = allocate(sizeof (struct nicdata));
1321 | p->name = new_string(if_name);
1322 |
1323 | if (state == HEAD) {
1324 | /* prevp will point to the last struct in the list */
1325 | if (prevp)
1326 | prevp->next = p;
1327 | else
1328 | *headp = p;
1329 | } else {
1330 | /* Insert new entry after **lastp */
1331 | prevp = (*lastp)->next;
1332 | (*lastp)->next = p;
1333 | p->next = prevp;
1334 | }
1335 |
1336 | *lastp = p;
1337 | return (p);
1338 | }
1339 | #endif /* OS_LINUX */
1340 |
1341 | #ifdef OS_LINUX
1342 | static int
1343 | find_interface_speed(struct nicdata *nicp)
1344 | {
1345 | struct if_speed_list *if_speed_list_ptr;
1346 |
1347 | if_speed_list_ptr = g_if_speed_list;
1348 |
1349 | while (if_speed_list_ptr != NULL) {
1350 | if (streql(nicp->name, if_speed_list_ptr->name)) {
1351 | nicp->speed = if_speed_list_ptr->speed;
1352 | nicp->duplex = if_speed_list_ptr->duplex;
1353 | return (B_TRUE);
1354 | }
1355 | if_speed_list_ptr = if_speed_list_ptr->next;
1356 | }
1357 | nicp->speed = 0;
1358 | nicp->duplex = DUPLEX_UNKNOWN;
1359 | return (B_FALSE);
1360 | }
1361 | #endif /* OS_LINUX */
1362 |
1363 | #ifdef OS_SOLARIS
1364 |
1365 | #define TCP_UPDATE(field, kstat_name) \
1366 | g_tcp_new->field = fetch64(g_tcp_ksp, kstat_name, 0);
1367 | #define UDP_UPDATE(field, kstat_name) \
1368 | g_udp_new->field = fetch64(g_udp_ksp, kstat_name, 0);
1369 |
1370 | /*
1371 | * update_stats - update stats for interfaces we are tracking
1372 | */
1373 | static void
1374 | update_stats()
1375 | {
1376 | struct nicdata *nicp;
1377 | struct timeval now_tv;
1378 |
1379 | (void) gettimeofday(&now_tv, NULL);
1380 |
1381 | if (g_tcp) {
1382 | /* Update TCP stats */
1383 | if (g_new_kstat_chain) {
1384 | g_tcp_ksp = kstat_lookup(g_kc, "tcp", -1, "tcp");
1385 | if (! g_tcp_ksp)
1386 | die(1, "kstat_lookup");
1387 | }
1388 | if (kstat_read(g_kc, g_tcp_ksp, NULL) < 0)
1389 | die(1, "kstat_read");
1390 | g_tcp_new->tv.tv_sec = now_tv.tv_sec;
1391 | g_tcp_new->tv.tv_usec = now_tv.tv_usec;
1392 | TCP_UPDATE(inDataInorderSegs, "inDataInorderSegs");
1393 | TCP_UPDATE(outDataSegs, "outDataSegs");
1394 | TCP_UPDATE(inDataInorderBytes, "inDataInorderBytes");
1395 | TCP_UPDATE(inDataUnorderSegs, "inDataUnorderSegs");
1396 | TCP_UPDATE(inDataUnorderBytes, "inDataUnorderBytes");
1397 | TCP_UPDATE(outDataBytes, "outDataBytes");
1398 | TCP_UPDATE(estabResets, "estabResets");
1399 | TCP_UPDATE(outRsts, "outRsts");
1400 | TCP_UPDATE(attemptFails, "attemptFails");
1401 | TCP_UPDATE(retransBytes, "retransBytes");
1402 | TCP_UPDATE(passiveOpens, "passiveOpens");
1403 | TCP_UPDATE(activeOpens, "activeOpens");
1404 | TCP_UPDATE(halfOpenDrop, "halfOpenDrop");
1405 | TCP_UPDATE(listenDrop, "listenDrop");
1406 | TCP_UPDATE(listenDropQ0, "listenDropQ0");
1407 | }
1408 | if (g_udp) {
1409 | /* Update UDP stats */
1410 | if (g_new_kstat_chain) {
1411 | g_udp_ksp = kstat_lookup(g_kc, "udp", -1, "udp");
1412 | if (! g_udp_ksp)
1413 | die(1, "kstat_lookup");
1414 | }
1415 | if (kstat_read(g_kc, g_udp_ksp, NULL) < 0)
1416 | die(1, "kstat_read");
1417 | g_udp_new->tv.tv_sec = now_tv.tv_sec;
1418 | g_udp_new->tv.tv_usec = now_tv.tv_usec;
1419 | UDP_UPDATE(inDatagrams, "inDatagrams");
1420 | UDP_UPDATE(outDatagrams, "outDatagrams");
1421 | UDP_UPDATE(inErrors, "inErrors");
1422 | UDP_UPDATE(outErrors, "outErrors");
1423 | }
1424 |
1425 | if (g_style == STYLE_NONE && ! g_list)
1426 | return;
1427 |
1428 | /* Update interface stats */
1429 | for (nicp = g_nicdatap; nicp; nicp = nicp->next) {
1430 | if (! (nicp->flags & NIC_UP))
1431 | /* Link is not up */
1432 | continue;
1433 | if (g_nonlocal && (nicp->flags & NIC_LOOPBACK))
1434 | continue;
1435 | if (! (nicp->flags & NIC_OK_UPDATED))
1436 | if (kstat_read(g_kc, nicp->op_ksp, NULL) < 0)
1437 | die(1, "kstat_read");
1438 | /* Save network values */
1439 | nicp->new.tv.tv_sec = now_tv.tv_sec;
1440 | nicp->new.tv.tv_usec = now_tv.tv_usec;
1441 | nicp->new.rbytes =
1442 | fetch6432(nicp->op_ksp, "rbytes64", "rbytes", 0);
1443 | nicp->new.wbytes =
1444 | fetch6432(nicp->op_ksp, "obytes64", "obytes", 0);
1445 | nicp->new.rpackets =
1446 | fetch6432(nicp->op_ksp, "ipackets64", "ipackets", 0);
1447 | nicp->new.wpackets =
1448 | fetch6432(nicp->op_ksp, "opackets64", "opackets", 0);
1449 | switch (g_style) {
1450 | case STYLE_EXTENDED_PARSEABLE:
1451 | case STYLE_EXTENDED:
1452 | nicp->new.ierr = fetch32(nicp->op_ksp, "ierrors", 0);
1453 | nicp->new.oerr = fetch32(nicp->op_ksp, "oerrors", 0);
1454 | /*FALLTHROUGH*/
1455 | case STYLE_FULL:
1456 | case STYLE_SUMMARY:
1457 | nicp->new.coll = fetch32(nicp->op_ksp, "collisions",
1458 | 0);
1459 | nicp->new.nocp = fetch_nocanput(nicp->op_ksp, 0);
1460 | nicp->new.defer = fetch32(nicp->op_ksp, "defer_xmts",
1461 | 0);
1462 | nicp->new.sat = nicp->new.defer + nicp->new.nocp +
1463 | nicp->new.coll;
1464 | nicp->new.sat += fetch32(nicp->op_ksp, "noxmtbuf", 0);
1465 | break;
1466 | }
1467 | nicp->speed = fetch64(nicp->op_ksp, "ifspeed", 0);
1468 | nicp->duplex = fetch32(nicp->op_ksp, "link_duplex", 0);
1469 | }
1470 |
1471 | }
1472 | #endif /* OS_SOLARIS */
1473 |
1474 | #ifdef OS_LINUX
1475 | /*
1476 | * load_netstat() - Reads PROC_NET_NETSTAT_PATH to get TCP stat(s)
1477 | */
1478 |
1479 | static void
1480 | load_netstat(FILE *netstat)
1481 | {
1482 | char buf[2048];
1483 | char *p;
1484 | int remaining;
1485 | long long ll[2];
1486 |
1487 | if (fseek(netstat, 0, SEEK_SET) != 0)
1488 | die(1, "fseek: %s", PROC_NET_NETSTAT_PATH);
1489 | remaining = 1;
1490 | while (remaining) {
1491 | p = fgets(buf, sizeof (buf), netstat);
1492 | if (! p)
1493 | break;
1494 | if (g_tcp && strncmp("TcpExt: SyncookiesSent SyncookiesRecv "
1495 | "SyncookiesFailed EmbryonicRsts PruneCalled RcvPruned "
1496 | "OfoPruned OutOfWindowIcmps LockDroppedIcmps "
1497 | "ArpFilter TW TWRecycled TWKilled PAWSPassive "
1498 | "PAWSActive PAWSEstab DelayedACKs DelayedACKLocked "
1499 | "DelayedACKLost ListenOverflows ListenDrops ",
1500 | p, 273) == 0) {
1501 | /* We are after field 20 and 21 */
1502 | int n = fscanf(netstat, "TcpExt: %*d %*d %*d "
1503 | "%*d %*d %*d %*d %*d %*d %*d "
1504 | "%*d %*d %*d %*d %*d %*d %*d "
1505 | "%*d %*d %lld %lld ",
1506 | &ll[0], &ll[1]);
1507 | if (n == 2)
1508 | g_tcp_new->listenDrop = ll[0] + ll[1];
1509 | remaining--;
1510 | }
1511 | }
1512 | }
1513 |
1514 |
1515 | /*
1516 | * load_snmp() - Reads PROC_NET_SNMP_PATH to get TCP & UDP stats
1517 | */
1518 |
1519 | static void
1520 | load_snmp(FILE *snmp)
1521 | {
1522 | char buf[2048];
1523 | char *p;
1524 | int remaining;
1525 | long long ll[14];
1526 |
1527 | /* Load TCP and/or UDP stats from /proc/net/snmp */
1528 | if (fseek(snmp, 0, SEEK_SET) != 0)
1529 | die(1, "fseek: %s", PROC_NET_SNMP_PATH);
1530 | remaining = 0;
1531 | if (g_tcp)
1532 | remaining++;
1533 | if (g_udp)
1534 | remaining++;
1535 | while (remaining) {
1536 | p = fgets(buf, sizeof (buf), snmp);
1537 | if (! p)
1538 | break;
1539 | if (g_tcp && strncmp("Tcp: RtoAlgorithm RtoMin RtoMax MaxConn "
1540 | "ActiveOpens PassiveOpens AttemptFails "
1541 | "EstabResets CurrEstab InSegs OutSegs "
1542 | "RetransSegs InErrs OutRsts", p, 141) == 0) {
1543 | int n;
1544 | n = fscanf(snmp, "Tcp: %lld %lld %lld %lld "
1545 | "%lld %lld %lld %lld %lld %lld "
1546 | "%lld %lld %lld %lld\n",
1547 | &ll[0], &ll[1], &ll[2], &ll[3],
1548 | &ll[4], &ll[5], &ll[6], &ll[7],
1549 | &ll[8], &ll[9], &ll[10], &ll[11],
1550 | &ll[12], &ll[13]);
1551 | if (n == 14) {
1552 | g_tcp_new->inDataInorderSegs = ll[9];
1553 | g_tcp_new->outDataSegs = ll[10];
1554 | g_tcp_new->estabResets = ll[7];
1555 | g_tcp_new->outRsts = ll[13];
1556 | g_tcp_new->attemptFails = ll[6];
1557 | /* Note: bytes */
1558 | g_tcp_new->retransBytes = ll[11];
1559 | g_tcp_new->passiveOpens = ll[5];
1560 | g_tcp_new->activeOpens = ll[4];
1561 | }
1562 | remaining--;
1563 | } else if (g_udp && strncmp("Udp: InDatagrams NoPorts "
1564 | "InErrors OutDatagrams RcvbufErrors "
1565 | "SndbufErrors\n", p, 72) == 0) {
1566 | int n;
1567 | n = fscanf(snmp, "Udp: %lld %lld %lld %lld "
1568 | "%lld %lld\n",
1569 | &ll[0], &ll[1], &ll[2], &ll[3],
1570 | &ll[4], &ll[5]);
1571 | if (n == 6) {
1572 | g_udp_new->inDatagrams = ll[0];
1573 | g_udp_new->outDatagrams = ll[3];
1574 | g_udp_new->inErrors = ll[2]; /* + ll[4]? */
1575 | g_udp_new->outErrors = ll[5];
1576 | }
1577 | remaining--;
1578 | }
1579 | }
1580 | }
1581 |
1582 | #endif /* OS_LINUX */
1583 |
1584 | #ifdef OS_LINUX
1585 | static void
1586 | get_speed_duplex(nicdata_t *nicp)
1587 | {
1588 | struct ifreq ifr;
1589 | struct ethtool_cmd edata;
1590 | int status;
1591 |
1592 | if (find_interface_speed(nicp))
1593 | return;
1594 |
1595 | if (nicp->flags & NIC_NO_GSET) {
1596 | if (nicp->speed > 0)
1597 | /* Already got something */
1598 | return;
1599 | if (nicp->flags & NIC_NO_SFLAG)
1600 | return;
1601 | if (! find_interface_speed(nicp))
1602 | nicp->flags |= NIC_NO_SFLAG;
1603 | return;
1604 | }
1605 |
1606 | /* Try SIOCETHTOOL */
1607 | strncpy(ifr.ifr_name, nicp->name, sizeof (ifr.ifr_name));
1608 | ifr.ifr_data = (void *) &edata;
1609 | edata.cmd = ETHTOOL_GSET;
1610 | status = ioctl(g_sock, SIOCETHTOOL, &ifr);
1611 | if (status < 0) {
1612 | nicp->flags |= NIC_NO_GSET;
1613 | get_speed_duplex(nicp);
1614 | return;
1615 | }
1616 | nicp->speed = (long long) edata.speed * 1000000;
1617 | nicp->duplex = edata.duplex;
1618 | }
1619 | #endif /* OS_LINUX */
1620 |
1621 | #ifdef OS_LINUX
1622 |
1623 | /*
1624 | * update_stats - update stats for interfaces we are tracking
1625 | */
1626 | static void
1627 | update_stats(int net_dev)
1628 | {
1629 | struct nicdata *nicp, *lastp;
1630 | struct timeval now_tv;
1631 | static int validated_format = 0;
1632 | static char proc_net_buffer[PROC_NET_BUFSIZ];
1633 | char *bufp;
1634 | int bufsiz, buf_remain, ret, n, skip_to_newline;
1635 | unsigned long long ll[16];
1636 | char if_name[32];
1637 | int loopback;
1638 |
1639 | /*
1640 | * Load PROC_NET_DEV
1641 | */
1642 | if (lseek(net_dev, 0, SEEK_SET) != 0)
1643 | die(1, "lseek: %s", PROC_NET_DEV_PATH);
1644 | bufsiz = read(net_dev, (void *) proc_net_buffer,
1645 | sizeof (proc_net_buffer));
1646 | if (bufsiz < 0)
1647 | die(1, "read: %s", PROC_NET_DEV_PATH);
1648 | else if (bufsiz < 200)
1649 | die(0, "%s: invalid format\n", PROC_NET_DEV_PATH);
1650 |
1651 | /*
1652 | * Validate if we have not previously done so
1653 | */
1654 | if (! validated_format) {
1655 | if (strncmp(proc_net_buffer,
1656 | "Inter-| Receive "
1657 | " | Transmit\n"
1658 | " face |bytes packets errs drop fifo frame compressed"
1659 | " multicast|bytes packets errs drop fifo colls carrier"
1660 | " compressed\n", 200) != 0)
1661 | die(0, "%s: invalid format\n",
1662 | PROC_NET_DEV_PATH);
1663 | else
1664 | validated_format++;
1665 | }
1666 |
1667 | /* Terminate our string */
1668 | bufp = proc_net_buffer + 200;
1669 | buf_remain = bufsiz - 200;
1670 | bufp[buf_remain + 1] = '\0';
1671 |
1672 | (void) gettimeofday(&now_tv, NULL);
1673 |
1674 | skip_to_newline = 0;
1675 | g_nicdata_count = 0;
1676 | lastp = NULL;
1677 | while (*bufp) {
1678 | if (skip_to_newline) {
1679 | /* Need to skip over previous data */
1680 | for (; *bufp; bufp++)
1681 | if (*bufp == '\n') {
1682 | bufp++;
1683 | break;
1684 | }
1685 | if (! *bufp)
1686 | break;
1687 | }
1688 | skip_to_newline = 1;
1689 |
1690 | /* Get the interface name */
1691 | while (*bufp == ' ')
1692 | bufp++;
1693 | /* Check the format */
1694 | n = strcspn(bufp, ":");
1695 | if (n >= sizeof (if_name))
1696 | die(0, "%s: interface name too long",
1697 | PROC_NET_DEV_PATH);
1698 | (void) strncpy(if_name, bufp, n);
1699 | if_name[n] = '\0';
1700 | /*
1701 | * Skip interface if not specifically interested in it
1702 | */
1703 | if (if_is_ignored(if_name)) {
1704 | continue;
1705 | }
1706 | /*
1707 | * If g_nonlocal, skip "lo"
1708 | */
1709 | loopback = streql("lo", if_name);
1710 | if (g_nonlocal && loopback)
1711 | continue;
1712 |
1713 | /* Scan in values */
1714 | bufp += n + 1;
1715 | ret = sscanf(bufp, "%llu %llu %llu %llu %llu %llu %llu"
1716 | " %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
1717 | &ll[0], &ll[1], &ll[2], &ll[3], &ll[4], &ll[5],
1718 | &ll[6], &ll[7], &ll[8], &ll[9], &ll[10], &ll[11],
1719 | &ll[12], &ll[13], &ll[14], &ll[15]);
1720 | if (ret != 16)
1721 | die(0, "%s: invalid format", PROC_NET_DEV_PATH);
1722 | /*
1723 | * Skip interface if it has never seen a packet
1724 | */
1725 | if (ll[1] == 0 && ll[9] == 0)
1726 | continue;
1727 |
1728 | /*
1729 | * OK, we'll keep this one
1730 | */
1731 | g_nicdata_count++;
1732 | nicp = find_nicdatap(&g_nicdatap, &lastp, if_name);
1733 | nicp->new.tv.tv_sec = now_tv.tv_sec;
1734 | nicp->new.tv.tv_usec = now_tv.tv_usec;
1735 | nicp->new.rbytes = ll[0];
1736 | nicp->new.rpackets = ll[1];
1737 | nicp->new.wbytes = ll[8];
1738 | nicp->new.wpackets = ll[9];
1739 | nicp->new.sat = ll[2];
1740 | nicp->new.sat += ll[3];
1741 | nicp->new.sat += ll[11];
1742 | nicp->new.sat += ll[12];
1743 | nicp->new.sat += ll[13];
1744 | nicp->new.sat += ll[14];
1745 | if (g_opt_x) {
1746 | nicp->new.ierr = ll[2];
1747 | nicp->new.oerr = ll[10];
1748 | nicp->new.coll = ll[13];
1749 | }
1750 | if (loopback)
1751 | nicp->flags |= NIC_LOOPBACK;
1752 | get_speed_duplex(nicp);
1753 | nicp->report = 1;
1754 | }
1755 | if (g_tcp || g_udp)
1756 | load_snmp(g_snmp);
1757 | if (g_tcp) {
1758 | g_tcp_new->tv = now_tv;
1759 | load_netstat(g_netstat);
1760 | }
1761 | if (g_udp)
1762 | g_udp_new->tv = now_tv;
1763 | }
1764 | #endif /* OS_LINUX */
1765 |
1766 | /*
1767 | * precision - figure an optimal floating precision for a printf()
1768 | */
1769 | static inline int
1770 | precision(double value)
1771 | {
1772 | if (value < 100)
1773 | return (2);
1774 | else if (value < 100000)
1775 | return (1);
1776 | return (0);
1777 | }
1778 |
1779 | static inline int
1780 | precision4(double value)
1781 | {
1782 | if (value < 10)
1783 | return (2);
1784 | else if (value < 100)
1785 | return (1);
1786 | return (0);
1787 | }
1788 |
1789 | static inline int
1790 | precision_p(double value)
1791 | {
1792 | if (value < 100)
1793 | if (value < 10)
1794 | return (3);
1795 | else
1796 | return (2);
1797 | else if (value < 10000)
1798 | return (1);
1799 | return (0);
1800 | }
1801 |
1802 | static char g_timestr[16];
1803 |
1804 | static void
1805 | update_timestr(time_t *tptr)
1806 | {
1807 | struct tm *tm;
1808 | time_t t;
1809 |
1810 | if (tptr)
1811 | t = *tptr;
1812 | else
1813 | t = time(NULL);
1814 | tm = localtime(&t);
1815 | (void) strftime(g_timestr, sizeof (g_timestr), "%H:%M:%S", tm);
1816 | }
1817 |
1818 | static uint32_t
1819 | tcpudpstat(uint32_t new, uint32_t old)
1820 | {
1821 | if (new < old)
1822 | return((UINT32_MAX - old) + new);
1823 | else
1824 | return(new - old);
1825 | }
1826 |
1827 | #define TCPSTAT(field) tcpudpstat(((g_tcp_new)->field), ((g_tcp_old)->field))
1828 | #define UDPSTAT(field) tcpudpstat(((g_udp_new)->field), ((g_udp_old)->field))
1829 |
1830 | static void
1831 | print_tcp()
1832 | {
1833 | double tdiff;
1834 | uint64_t resets;
1835 | double retrans_rate;
1836 | uint64_t outbytes;
1837 | double inkb, outkb, inseg, outseg, reset, attfail, inconn,
1838 | outconn, drops;
1839 | tcpstats_t *tsp;
1840 |
1841 | if (g_tcp_old->tv.tv_sec == 0)
1842 | /* Not initialised */
1843 | g_tcp_old->tv.tv_sec = fetch_boot_time();
1844 | /* g_tcp_old->tv.tv_sec = g_tcp_new->tv.tv_sec - 100; */
1845 | tdiff = tv_diff(&g_tcp_new->tv, &g_tcp_old->tv);
1846 | if (tdiff == 0)
1847 | tdiff = 1;
1848 |
1849 | /* Header */
1850 | update_timestr(&(g_tcp_new->tv.tv_sec));
1851 | if (! g_opt_p)
1852 | (void) printf("%8s %7s %7s %7s %7s %5s %5s %4s %5s %5s %5s\n",
1853 | g_timestr, "InKB", "OutKB", "InSeg", "OutSeg",
1854 | "Reset", "AttF", "%ReTX", "InConn", "OutCon", "Drops");
1855 |
1856 | resets = (TCPSTAT(estabResets) + TCPSTAT(outRsts));
1857 | outbytes = TCPSTAT(outDataBytes);
1858 |
1859 | inkb = (TCPSTAT(inDataInorderBytes) + TCPSTAT(inDataUnorderBytes)) /
1860 | 1024.0 / tdiff;
1861 | outkb = outbytes / 1024.0 / tdiff;
1862 | inseg = (TCPSTAT(inDataInorderSegs) + TCPSTAT(inDataUnorderSegs)) /
1863 | tdiff;
1864 | outseg = TCPSTAT(outDataSegs) / tdiff;
1865 | reset = resets / tdiff;
1866 | attfail = TCPSTAT(attemptFails) / tdiff;
1867 | if (outbytes == 0)
1868 | retrans_rate = 0.0;
1869 | else
1870 | retrans_rate = TCPSTAT(retransBytes) * 100.0 /
1871 | (double)outbytes;
1872 | inconn = TCPSTAT(passiveOpens) / tdiff;
1873 | outconn = TCPSTAT(activeOpens) / tdiff;
1874 | drops = (TCPSTAT(halfOpenDrop) + TCPSTAT(listenDrop) +
1875 | TCPSTAT(listenDropQ0)) / tdiff;
1876 |
1877 | #ifdef NOTDEBUG
1878 | double ods_rate = (g_tcp_new->outDataSegs - g_tcp_old->outDataSegs) /
1879 | tdiff;
1880 | (void) printf("old->outDataSegs = %llu, new->outDataSegs = %llu, "
1881 | " tdiff = %7.2f; rate = %7.2f\n",
1882 | g_tcp_old->outDataSegs, g_tcp_new->outDataSegs,
1883 | tdiff, ods_rate);
1884 | #endif /* DEBUG */
1885 | if (g_opt_p)
1886 | (void) printf("%ld:TCP:%.*f:%.*f:%.*f:%.*f:%.*f:%.*f:"
1887 | "%.*f:%.*f:%.*f:%.*f\n",
1888 | g_tcp_new->tv.tv_sec,
1889 | precision_p(inkb), inkb,
1890 | precision_p(outkb), outkb,
1891 | precision_p(inseg), inseg,
1892 | precision_p(outseg), outseg,
1893 | precision_p(reset), reset,
1894 | precision_p(attfail), attfail,
1895 | precision_p(retrans_rate), retrans_rate,
1896 | precision_p(inconn), inconn,
1897 | precision_p(outconn), outconn,
1898 | precision_p(drops), drops);
1899 | else
1900 | (void) printf("TCP %7.*f %7.*f %7.*f %7.*f %5.*f %5.*f "
1901 | "%4.*f %6.*f %6.*f %5.*f\n",
1902 | precision(inkb), inkb,
1903 | precision(outkb), outkb,
1904 | precision(inseg), inseg,
1905 | precision(outseg), outseg,
1906 | precision4(reset), reset,
1907 | precision4(attfail), attfail,
1908 | precision_p(retrans_rate), retrans_rate,
1909 | precision4(inconn), inconn,
1910 | precision4(outconn), outconn,
1911 | precision4(drops), drops);
1912 | /* Flip pointers to TCP stats */
1913 | tsp = g_tcp_old;
1914 | g_tcp_old = g_tcp_new;
1915 | g_tcp_new = tsp;
1916 | }
1917 |
1918 | static void
1919 | print_udp()
1920 | {
1921 | double indg, outdg, inerr, outerr;
1922 | udpstats_t *usp;
1923 | double tdiff;
1924 |
1925 | if (g_udp_old->tv.tv_sec == 0)
1926 | /* Not initialised */
1927 | g_udp_old->tv.tv_sec = fetch_boot_time();
1928 | tdiff = tv_diff(&g_udp_new->tv, &g_udp_old->tv);
1929 | if (tdiff == 0)
1930 | tdiff = 1;
1931 |
1932 | /* Header */
1933 | update_timestr(&(g_udp_new->tv.tv_sec));
1934 | if (! g_opt_p)
1935 | (void) printf("%8s %7s %7s %7s %7s\n",
1936 | g_timestr, "InDG", "OutDG", "InErr", "OutErr");
1937 |
1938 | indg = UDPSTAT(inDatagrams) / tdiff;
1939 | outdg = UDPSTAT(outDatagrams) / tdiff;
1940 | inerr = UDPSTAT(inErrors) / tdiff;
1941 | outerr = UDPSTAT(outErrors) / tdiff;
1942 |
1943 | if (g_opt_p)
1944 | (void) printf("%ld:UDP:%.*f:%.*f:%.*f:%.*f\n",
1945 | g_udp_new->tv.tv_sec,
1946 | precision_p(indg), indg,
1947 | precision_p(outdg), outdg,
1948 | precision_p(inerr), inerr,
1949 | precision_p(outerr), outerr);
1950 | else
1951 | (void) printf("UDP "
1952 | "%7.*f %7.*f %7.*f %7.*f\n",
1953 | precision(indg), indg,
1954 | precision(outdg), outdg,
1955 | precision(inerr), inerr,
1956 | precision(outerr), outerr);
1957 |
1958 | /* Flip pointers to TCP stats */
1959 | usp = g_udp_old;
1960 | g_udp_old = g_udp_new;
1961 | g_udp_new = usp;
1962 | }
1963 |
1964 | /*
1965 | * print_header - print the header line.
1966 | */
1967 | static void
1968 | print_header(void)
1969 | {
1970 | #if DEBUG > 1
1971 | (void) printf("<>\n", g_nicdata_count);
1972 | #endif
1973 | switch (g_style) {
1974 | case STYLE_SUMMARY:
1975 | (void) printf("%8s %8s %14s %14s\n",
1976 | "Time", "Int", g_runit_1, g_wunit_1);
1977 | break;
1978 | case STYLE_FULL:
1979 | (void) printf("%8s %8s %7s %7s %7s "
1980 | "%7s %7s %7s %5s %6s\n",
1981 | "Time", "Int", g_runit_1, g_wunit_1, "rPk/s",
1982 | "wPk/s", "rAvs", "wAvs", "%Util", "Sat");
1983 | break;
1984 | case STYLE_FULL_UTIL:
1985 | (void) printf("%8s %8s %7s %7s %7s "
1986 | "%7s %7s %7s %6s %6s\n",
1987 | "Time", "Int", g_runit_1, g_wunit_1, "rPk/s",
1988 | "wPk/s", "rAvs", "wAvs", "%rUtil", "%wUtil");
1989 | break;
1990 | case STYLE_EXTENDED:
1991 | update_timestr(NULL);
1992 | (void) printf("%-10s %7s %7s %7s %7s "
1993 | "%5s %5s %5s %5s %5s %5s\n",
1994 | g_timestr, g_runit_2, g_wunit_2, "RdPkt", "WrPkt",
1995 | "IErr", "OErr", "Coll", "NoCP", "Defer", "%Util");
1996 | break;
1997 | case STYLE_EXTENDED_UTIL:
1998 | update_timestr(NULL);
1999 | (void) printf("%-10s %7s %7s %7s %7s "
2000 | "%5s %5s %5s %5s %5s %6s %6s\n",
2001 | g_timestr, g_runit_2, g_wunit_2, "RdPkt", "WrPkt",
2002 | "IErr", "OErr", "Coll", "NoCP", "Defer",
2003 | "%rUtil", "%wUtil");
2004 | break;
2005 | }
2006 | }
2007 |
2008 | inline static double
2009 | max(double d1, double d2)
2010 | {
2011 | if (d1 > d2)
2012 | return (d1);
2013 | return (d2);
2014 | }
2015 |
2016 | inline static double
2017 | min(double d1, double d2)
2018 | {
2019 | if (d1 < d2)
2020 | return (d1);
2021 | return (d2);
2022 | }
2023 |
2024 | /*
2025 | * print_stats - generate output
2026 | *
2027 | * This routine runs through the linked list of interfaces, prints out
2028 | * statistics where appropriate, then moves the "new" stats to the "old"
2029 | * stats, ready for next time.
2030 | */
2031 | static void
2032 | print_stats()
2033 | {
2034 | struct nicdata *nicp; /* ptr into g_nicdatap linked list */
2035 | double rbps; /* read bytes per sec */
2036 | double wbps; /* write bytes per sec */
2037 | double rkps; /* read KB per sec */
2038 | double wkps; /* write KB per sec */
2039 | double rpps; /* read packets per sec */
2040 | double wpps; /* write packets per sec */
2041 | double ravs; /* read average packet size */
2042 | double wavs; /* write average packet size */
2043 | double sats; /* saturation value per sec */
2044 | double ierrs;
2045 | double oerrs;
2046 | double colls;
2047 | double nocps;
2048 | double defers;
2049 | double tdiff; /* time difference between samples */
2050 | double util; /* utilisation */
2051 | double rutil; /* In (read) utilisation */
2052 | double wutil; /* Out (write) utilisation */
2053 |
2054 | if (g_tcp)
2055 | print_tcp();
2056 | if (g_udp)
2057 | print_udp();
2058 |
2059 | /* Print header if needed */
2060 | if (! g_list)
2061 | if (g_tcp || g_udp || (g_line >= PAGE_SIZE)) {
2062 | g_line = 0;
2063 | print_header();
2064 | }
2065 |
2066 | for (nicp = g_nicdatap; nicp; nicp = nicp->next) {
2067 | #ifdef OS_SOLARIS
2068 | if (! (nicp->flags & NIC_UP))
2069 | /* Link is not up */
2070 | continue;
2071 | if (g_nonlocal && (nicp->flags & NIC_LOOPBACK))
2072 | continue;
2073 | #endif
2074 | #ifdef OS_LINUX
2075 | if (! nicp->report)
2076 | continue;
2077 | nicp->report = 0;
2078 | #endif
2079 | /* Calculate time difference */
2080 | #ifdef OS_LINUX
2081 | if (nicp->old.tv.tv_sec == 0)
2082 | /* Not initialised, so numbers will be since boot */
2083 | nicp->old.tv.tv_sec = g_boot_time;
2084 | #endif
2085 | tdiff = tv_diff(&nicp->new.tv, &nicp->old.tv);
2086 | if (tdiff == 0)
2087 | tdiff = 1;
2088 |
2089 | /* Calculate per second values */
2090 | rbps = (nicp->new.rbytes - nicp->old.rbytes) / tdiff;
2091 | wbps = (nicp->new.wbytes - nicp->old.wbytes) / tdiff;
2092 | rpps = (nicp->new.rpackets - nicp->old.rpackets) / tdiff;
2093 | wpps = (nicp->new.wpackets - nicp->old.wpackets) / tdiff;
2094 | if (g_style == STYLE_EXTENDED ||
2095 | g_style == STYLE_EXTENDED_UTIL ||
2096 | g_style == STYLE_EXTENDED_PARSEABLE) {
2097 | ierrs = (nicp->new.ierr - nicp->old.ierr) / tdiff;
2098 | oerrs = (nicp->new.oerr - nicp->old.oerr) / tdiff;
2099 | colls = (nicp->new.coll - nicp->old.coll) / tdiff;
2100 | nocps = (nicp->new.nocp - nicp->old.nocp) / tdiff;
2101 | defers = (nicp->new.defer - nicp->old.defer) / tdiff;
2102 | } else if (g_style == STYLE_FULL ||
2103 | g_style == STYLE_FULL_UTIL) {
2104 | if (rpps > 0)
2105 | ravs = rbps / rpps;
2106 | else
2107 | ravs = 0;
2108 | if (wpps > 0)
2109 | wavs = wbps / wpps;
2110 | else
2111 | wavs = 0;
2112 | }
2113 | if (g_style == STYLE_FULL ||
2114 | g_style == STYLE_FULL_UTIL ||
2115 | g_style == STYLE_PARSEABLE ||
2116 | g_style == STYLE_EXTENDED_PARSEABLE)
2117 | sats = (nicp->new.sat - nicp->old.sat) / tdiff;
2118 | if (g_opt_m) {
2119 | /* report in Mbps */
2120 | rkps = rbps / 1024 / 128;
2121 | wkps = wbps / 1024 / 128;
2122 | } else {
2123 | /* original KB/sec */
2124 | rkps = rbps / 1024;
2125 | wkps = wbps / 1024;
2126 | }
2127 |
2128 | /* Calculate utilisation */
2129 | if (nicp->speed > 0) {
2130 | /*
2131 | * The following have a mysterious "800", it is
2132 | * 100 for the % conversion, and 8 for
2133 | * bytes2bits.
2134 | */
2135 | rutil = min(rbps * 800 / nicp->speed, 100);
2136 | wutil = min(wbps * 800 / nicp->speed, 100);
2137 | if (nicp->duplex == DUPLEX_FULL) {
2138 | /* Full duplex */
2139 | util = max(rutil, wutil);
2140 | } else {
2141 | /* Half Duplex */
2142 | util = min((rbps + wbps) * 800 / nicp->speed,
2143 | 100);
2144 | }
2145 | } else {
2146 | util = 0;
2147 | rutil = 0;
2148 | wutil = 0;
2149 | }
2150 | /* always print header if there are multiple NICs */
2151 | if (g_nicdata_count > 1)
2152 | g_line += PAGE_SIZE;
2153 | else
2154 | g_line++;
2155 |
2156 | /* Skip zero lines */
2157 | if (g_skipzero && wpps == 0 && rpps == 0)
2158 | continue;
2159 |
2160 | /* Print output line */
2161 | switch (g_style) {
2162 | case STYLE_SUMMARY:
2163 | update_timestr(&nicp->new.tv.tv_sec);
2164 | (void) printf("%s %8s %14.3f %14.3f\n",
2165 | g_timestr, nicp->name, rkps, wkps);
2166 | break;
2167 | case STYLE_FULL:
2168 | update_timestr(&nicp->new.tv.tv_sec);
2169 | (void) printf("%s %8s %7.*f %7.*f %7.*f %7.*f "
2170 | "%7.*f %7.*f %5.*f %6.*f\n",
2171 | g_timestr, nicp->name,
2172 | precision(rkps), rkps,
2173 | precision(wkps), wkps,
2174 | precision(rpps), rpps,
2175 | precision(wpps), wpps,
2176 | precision(ravs), ravs,
2177 | precision(wavs), wavs,
2178 | precision4(util), util,
2179 | precision(sats), sats);
2180 | break;
2181 | case STYLE_FULL_UTIL:
2182 | update_timestr(&nicp->new.tv.tv_sec);
2183 | (void) printf("%s %8s %7.*f %7.*f %7.*f %7.*f "
2184 | "%7.*f %7.*f %6.*f %6.*f\n",
2185 | g_timestr, nicp->name,
2186 | precision(rkps), rkps,
2187 | precision(wkps), wkps,
2188 | precision(rpps), rpps,
2189 | precision(wpps), wpps,
2190 | precision(ravs), ravs,
2191 | precision(wavs), wavs,
2192 | precision4(rutil), rutil,
2193 | precision4(wutil), wutil);
2194 | break;
2195 | case STYLE_PARSEABLE:
2196 | (void) printf("%ld:%s:%.*f:%.*f:%.*f:%.*f:"
2197 | "%.*f:%.*f\n",
2198 | nicp->new.tv.tv_sec, nicp->name,
2199 | precision_p(rkps), rkps,
2200 | precision_p(wkps), wkps,
2201 | precision_p(rpps), rpps,
2202 | precision_p(wpps), wpps,
2203 | precision4(util), util,
2204 | precision(sats), sats);
2205 | break;
2206 | case STYLE_EXTENDED:
2207 | (void) printf("%-10s %7.*f %7.*f %7.*f %7.*f "
2208 | "%5.*f %5.*f %5.*f %5.*f %5.*f %5.*f\n",
2209 | nicp->name,
2210 | precision(rkps), rkps,
2211 | precision(wkps), wkps,
2212 | precision(rpps), rpps,
2213 | precision(wpps), wpps,
2214 | precision4(ierrs), ierrs,
2215 | precision4(oerrs), oerrs,
2216 | precision4(colls), colls,
2217 | precision4(nocps), nocps,
2218 | precision4(defers), defers,
2219 | precision4(util), util);
2220 | break;
2221 | case STYLE_EXTENDED_UTIL:
2222 | (void) printf("%-10s %7.*f %7.*f %7.*f %7.*f "
2223 | "%5.*f %5.*f %5.*f %5.*f %5.*f %6.*f %6.*f\n",
2224 | nicp->name,
2225 | precision(rkps), rkps,
2226 | precision(wkps), wkps,
2227 | precision(rpps), rpps,
2228 | precision(wpps), wpps,
2229 | precision4(ierrs), ierrs,
2230 | precision4(oerrs), oerrs,
2231 | precision4(colls), colls,
2232 | precision4(nocps), nocps,
2233 | precision4(defers), defers,
2234 | precision4(rutil), rutil,
2235 | precision4(wutil), wutil);
2236 | break;
2237 | case STYLE_EXTENDED_PARSEABLE:
2238 | /*
2239 | * Use same initial order as STYLE_PARSEABLE
2240 | * for backward compatibility
2241 | */
2242 | (void) printf("%ld:%s:%.*f:%.*f:%.*f:%.*f:"
2243 | "%.*f:%.*f:%.*f:%.*f:%.*f:%.*f:%.*f\n",
2244 | nicp->new.tv.tv_sec, nicp->name,
2245 | precision_p(rkps), rkps,
2246 | precision_p(wkps), wkps,
2247 | precision_p(rpps), rpps,
2248 | precision_p(wpps), wpps,
2249 | precision4(util), util,
2250 | precision(sats), sats,
2251 | precision(ierrs), ierrs,
2252 | precision(oerrs), oerrs,
2253 | precision(colls), colls,
2254 | precision(nocps), nocps,
2255 | precision(defers), defers);
2256 | }
2257 |
2258 | /* Save the current values for next time */
2259 | nicp->old = nicp->new;
2260 | }
2261 | }
2262 |
2263 | static void
2264 | cont_handler(int sig_number)
2265 | {
2266 | /* Re-set the signal handler */
2267 | (void) signal(sig_number, cont_handler);
2268 | #if DEBUG > 0
2269 | (void) fprintf(stderr, "<< caught SIGCONT >>\n");
2270 | #endif
2271 | g_caught_cont = 1;
2272 | }
2273 |
2274 | #ifdef OS_SOLARIS
2275 | /*
2276 | * sleep_for - sleep until start_n + period
2277 | *
2278 | * This Solaris version uses gethrtime() and nanosleep()
2279 | */
2280 | static void
2281 | sleep_for(hrtime_t period, hrtime_t start_n)
2282 | {
2283 | struct timespec pause_tv;
2284 | hrtime_t now_n, pause_n;
2285 | int status;
2286 |
2287 | pause_n = period;
2288 | do {
2289 | pause_tv.tv_sec = pause_n / NANOSEC;
2290 | pause_tv.tv_nsec = pause_n % NANOSEC;
2291 | status = nanosleep(&pause_tv, (struct timespec *)NULL);
2292 | if (status < 0)
2293 | if (errno == EINTR) {
2294 | now_n = gethrtime();
2295 | pause_n = start_n + period - now_n;
2296 | if (pause_n < 100)
2297 | /* Forget about it */
2298 | return;
2299 | } else {
2300 | die(1, "nanosleep", g_progname);
2301 | }
2302 | } while (status != 0);
2303 | }
2304 | #endif /* OS_SOLARIS */
2305 |
2306 | #ifdef OS_LINUX
2307 | /*
2308 | * sleep_for - sleep until now + millisec
2309 | */
2310 | static inline void
2311 | sleep_for(int period_ms, struct timeval *start_tv)
2312 | {
2313 | int status;
2314 | int done = 0;
2315 | struct timeval then;
2316 | int us;
2317 |
2318 | then.tv_sec = 0;
2319 | do {
2320 | status = poll(NULL, 0, period_ms);
2321 | if (status < 0) {
2322 | if (errno != EINTR) {
2323 | perror("poll");
2324 | exit(1);
2325 | }
2326 | /* Interrupted - we are not done yet */
2327 | if (then.tv_sec == 0) {
2328 | then.tv_sec = start_tv->tv_sec +
2329 | (period_ms / 1000);
2330 | us = ((period_ms % 1000) * 1000) +
2331 | start_tv->tv_usec;
2332 | if (us > 1000000) {
2333 | /* Wrapped */
2334 | then.tv_sec++;
2335 | then.tv_usec = us - 1000000;
2336 | } else
2337 | then.tv_usec = us;
2338 | }
2339 | (void) gettimeofday(start_tv, NULL);
2340 | period_ms = (then.tv_sec - start_tv->tv_sec) * 1000;
2341 | period_ms += (then.tv_usec - start_tv->tv_usec) / 1000;
2342 | if (period_ms <= 0)
2343 | done = 1;
2344 | } else
2345 | done = 1;
2346 | } while (! done);
2347 | }
2348 | #endif /* OS_LINUX */
2349 |
2350 | #ifdef OS_LINUX
2351 | static void
2352 | init_if_speed_list(char *speed_list)
2353 | {
2354 | struct if_speed_list *list_elem;
2355 | char *speed_list_save_ptr;
2356 | char *if_record;
2357 | char name[32];
2358 | uint64_t speed;
2359 | char duplex_s[32];
2360 | int tokens;
2361 |
2362 | if_record = strtok_r(speed_list, ",", &speed_list_save_ptr);
2363 | while (if_record) {
2364 | duplex_s[0] = '\0';
2365 | tokens = sscanf(if_record, "%31[^:]:%llu%31s",
2366 | name, &speed, duplex_s);
2367 | if (tokens == 0)
2368 | continue;
2369 | if (speed <= 0)
2370 | die(0, "invalid speed for -S %s", name, if_record);
2371 | if (name == NULL)
2372 | die(0, "invalid -S argument");
2373 |
2374 | list_elem = allocate(sizeof (struct if_speed_list));
2375 | list_elem->name = new_string(name);
2376 | /* speed is in megabits/second */
2377 | list_elem->speed = speed * 1000000;
2378 | /* Do we have a duplex suffix? */
2379 | switch (duplex_s[0]) {
2380 | case 'h':
2381 | case 'H':
2382 | list_elem->duplex = DUPLEX_HALF;
2383 | break;
2384 | case 'f':
2385 | case 'F':
2386 | case '\0': /* Not specified - default is full */
2387 | list_elem->duplex = DUPLEX_FULL;
2388 | break;
2389 | default:
2390 | list_elem->duplex = DUPLEX_UNKNOWN;
2391 | }
2392 | #if DEBUG > 0
2393 | fprintf(stderr, "<< %s - %llu mbps, duplex = %d >>\n",
2394 | name, speed, list_elem->duplex);
2395 | #endif
2396 | list_elem->next = g_if_speed_list;
2397 | g_if_speed_list = list_elem;
2398 |
2399 | if_record = strtok_r(NULL, ",", &speed_list_save_ptr);
2400 | }
2401 | }
2402 | #endif /* OS_LINUX */
2403 |
2404 | /*
2405 | * split - Split a string of delimited fields, returning an array of char *
2406 | *
2407 | * NOTE: the input string gets modified by this routine
2408 | */
2409 | static char **
2410 | split(char *string, char *delim, int *nitems)
2411 | {
2412 | int ndelim, i;
2413 | char *p;
2414 | char *lasts;
2415 | char **ptrs;
2416 |
2417 | /* How many delimiters do we have? */
2418 | ndelim = 0;
2419 | for (p = string; *p; p++)
2420 | if (*p == *delim)
2421 | ndelim++;
2422 |
2423 | /* We need that many ptrs + 2 (max) */
2424 | ptrs = allocate((ndelim + 2) * sizeof (char *));
2425 |
2426 | /* Tokenize */
2427 | i = 0;
2428 | ptrs[i] = strtok_r(string, delim, &lasts);
2429 | while (ptrs[i])
2430 | ptrs[++i] = strtok_r(NULL, delim, &lasts);
2431 | *nitems = i;
2432 | return (ptrs);
2433 | }
2434 |
2435 | static char *
2436 | duplex_to_string(duplex_t duplex)
2437 | {
2438 | switch (duplex) {
2439 | case DUPLEX_HALF:
2440 | return ("half");
2441 | case DUPLEX_FULL:
2442 | return ("full");
2443 | default:
2444 | return ("unkn");
2445 | }
2446 | }
2447 |
2448 | static void
2449 | list_ifs()
2450 | {
2451 | nicdata_t *p;
2452 | int loopback;
2453 | uint64_t speed;
2454 | int verbose;
2455 | int if_up;
2456 |
2457 | #ifdef OS_SOLARIS
2458 | verbose = g_verbose;
2459 | #else
2460 | verbose = 0;
2461 | #endif
2462 |
2463 | if (verbose)
2464 | (void) printf("Int Loopback Mbit/s Duplex State"
2465 | " Flags ls_types op_types\n");
2466 | else
2467 | (void) printf("Int Loopback Mbit/s Duplex State\n");
2468 | for (p = g_nicdatap; p; p = p->next) {
2469 | if (if_is_ignored(p->name))
2470 | continue;
2471 | loopback = p->flags & NIC_LOOPBACK;
2472 | #ifdef OS_SOLARIS
2473 | if_up = p->flags & NIC_UP;
2474 | #else
2475 | if_up = B_TRUE;
2476 | #endif
2477 | if (loopback)
2478 | (void) printf("%-12s Yes - %4s %4s",
2479 | p->name, duplex_to_string(p->duplex),
2480 | if_up ? "up" : "down");
2481 | else {
2482 | speed = (p->speed) / 1000000;
2483 | (void) printf("%-12s No %8llu %4s %4s",
2484 | p->name, speed, duplex_to_string(p->duplex),
2485 | if_up ? "up" : "down");
2486 | }
2487 | #ifdef OS_SOLARIS
2488 | if (verbose) {
2489 | (void) printf(" %08x %08x %08x\n",
2490 | p->flags, p->ls_types, p->op_types);
2491 | continue;
2492 | }
2493 | #endif
2494 | (void) printf("\n");
2495 | }
2496 | }
2497 |
2498 | static void
2499 | init_tcp()
2500 | {
2501 | g_tcp_old = allocate(sizeof (tcpstats_t));
2502 | g_tcp_new = allocate(sizeof (tcpstats_t));
2503 | #ifdef OS_SOLARIS
2504 | g_tcp_ksp = kstat_lookup(g_kc, "tcp", -1, "tcp");
2505 | if (! g_tcp_ksp) {
2506 | diag(0, "tcp kstats not found");
2507 | }
2508 | #endif
2509 | }
2510 |
2511 | static void
2512 | init_udp()
2513 | {
2514 | g_udp_old = allocate(sizeof (udpstats_t));
2515 | g_udp_new = allocate(sizeof (udpstats_t));
2516 | #ifdef OS_SOLARIS
2517 | g_udp_ksp = kstat_lookup(g_kc, "udp", -1, "udp");
2518 | if (! g_udp_ksp)
2519 | diag(0, "udp kstats not found");
2520 | #endif
2521 | }
2522 |
2523 | #ifdef USE_DLADM
2524 | /*
2525 | * Do not be confused - the prefix "dl" can stand for Dynamic Linking,
2526 | * as well as "Data Link"...
2527 | */
2528 | static void
2529 | init_dladm()
2530 | {
2531 | void *handle;
2532 | dladm_status_t (*fptr)();
2533 | dladm_status_t dlstat;
2534 |
2535 | g_use_dladm = B_FALSE;
2536 | if ((handle = dlopen("libdladm.so.1", RTLD_LAZY)) == NULL)
2537 | return;
2538 | if ((fptr = (dladm_status_t (*)())
2539 | dlsym(handle, "dladm_datalink_id2info")) == NULL) {
2540 | (void) dlclose(handle);
2541 | return;
2542 | }
2543 | g_use_dladm = B_TRUE;
2544 | /* Get a handle to use for libdladm call(s) */
2545 | /* NOTE: This changed in S11.1 */
2546 | #ifdef NETADM_ACTIVE_PROFILE
2547 | dlstat = dladm_open(&g_handle, NULL);
2548 | #else
2549 | dlstat = dladm_open(&g_handle);
2550 | #endif
2551 | if (dlstat != DLADM_STATUS_OK) {
2552 | char errmsg[DLADM_STRSIZE];
2553 |
2554 | die(0, "could not open /dev/dld: %s",
2555 | dladm_status2str(dlstat, errmsg));
2556 | }
2557 | }
2558 | #endif /* USE_DLADM */
2559 |
2560 | /*
2561 | * Main Program
2562 | */
2563 | int
2564 | main(int argc, char **argv)
2565 | {
2566 | /*
2567 | * Variable Declaration
2568 | */
2569 | int interval; /* interval, secs */
2570 | int loop_max; /* max output lines */
2571 | int loop; /* current loop number */
2572 | int option; /* command line switch */
2573 | int tracked_ifs;
2574 | int time_is_up;
2575 | #ifdef OS_SOLARIS
2576 | hrtime_t period_n; /* period of each iteration in nanoseconds */
2577 | hrtime_t start_n; /* start point of an iteration, nsec */
2578 | hrtime_t end_n; /* end time of work in an iteration, nsec */
2579 | hrtime_t pause_n; /* time until start of next iteration, nsec */
2580 | kid_t kc_id;
2581 | #else /* OS_SOLARIS */
2582 | int net_dev; /* file descriptor for stats file */
2583 | int pause_m; /* time to pause, milliseconds */
2584 | struct timeval start; /* start point of an iteration */
2585 | struct timeval now;
2586 | #endif /* OS_SOLARIS */
2587 | #if DEBUG > 1
2588 | struct timeval debug_now;
2589 | #endif
2590 |
2591 | /* defaults */
2592 | interval = INTERVAL;
2593 | loop_max = LOOP_MAX;
2594 | g_line = PAGE_SIZE;
2595 | loop = 0;
2596 | g_style = STYLE_FULL;
2597 | g_skipzero = B_FALSE;
2598 | g_nonlocal = B_FALSE;
2599 | g_someif = B_FALSE;
2600 | g_forever = B_FALSE;
2601 | g_caught_cont = B_FALSE;
2602 | g_opt_m = B_FALSE;
2603 | #ifdef OS_SOLARIS
2604 | g_list = B_FALSE;
2605 | g_verbose = B_FALSE;
2606 | g_opt_x = B_FALSE;
2607 | g_opt_p = B_FALSE;
2608 | g_opt_k = B_FALSE;
2609 | #endif
2610 |
2611 | /*
2612 | * Process arguments
2613 | */
2614 | g_progname = argv[0];
2615 | while ((option = getopt(argc, argv, GETOPT_OPTIONS)) != -1) {
2616 | switch (option) {
2617 | case 'h':
2618 | usage();
2619 | break;
2620 | case 'i':
2621 | g_tracked = split(optarg, ",", &tracked_ifs);
2622 | g_someif = tracked_ifs > 0;
2623 | break;
2624 | case 's':
2625 | g_style = STYLE_SUMMARY;
2626 | break;
2627 | case 'v':
2628 | g_verbose = B_TRUE;
2629 | break;
2630 | case 'z':
2631 | g_skipzero = 1;
2632 | break;
2633 | case 'n':
2634 | g_nonlocal = 1;
2635 | break;
2636 | case 't':
2637 | g_tcp = B_TRUE;
2638 | if (g_style == STYLE_FULL)
2639 | g_style = STYLE_NONE;
2640 | break;
2641 | case 'u':
2642 | g_udp = B_TRUE;
2643 | if (g_style == STYLE_FULL)
2644 | g_style = STYLE_NONE;
2645 | break;
2646 | case 'x':
2647 | g_opt_x = B_TRUE;
2648 | break;
2649 | case 'a':
2650 | g_tcp = g_udp = B_TRUE;
2651 | if (g_style == STYLE_FULL)
2652 | g_opt_x = B_TRUE;
2653 | break;
2654 | case 'M':
2655 | case 'm': /* Undocumented */
2656 | g_opt_m = B_TRUE;
2657 | break;
2658 | case 'p':
2659 | g_opt_p = B_TRUE;
2660 | break;
2661 | case 'l':
2662 | g_list = B_TRUE;
2663 | g_style = STYLE_NONE;
2664 | break;
2665 | case 'U':
2666 | g_opt_U = B_TRUE;
2667 | break;
2668 | #ifdef OS_LINUX
2669 | case 'S':
2670 | init_if_speed_list(optarg);
2671 | break;
2672 | #endif
2673 | #ifdef OS_SOLARIS
2674 | case 'k':
2675 | g_opt_k = B_TRUE;
2676 | break;
2677 | #endif /* OS_SOLARIS */
2678 | default:
2679 | usage();
2680 | }
2681 | }
2682 | if (g_opt_p) {
2683 | if (g_opt_x)
2684 | g_style = STYLE_EXTENDED_PARSEABLE;
2685 | else if (! g_tcp && ! g_udp)
2686 | g_style = STYLE_PARSEABLE;
2687 | /* Always output KB in the parseable format */
2688 | g_opt_m = B_FALSE;
2689 | } else
2690 | if (g_opt_x)
2691 | g_style = STYLE_EXTENDED;
2692 | if (g_opt_U)
2693 | switch (g_style) {
2694 | case STYLE_FULL:
2695 | g_style = STYLE_FULL_UTIL;
2696 | break;
2697 | case STYLE_EXTENDED:
2698 | g_style = STYLE_EXTENDED_UTIL;
2699 | }
2700 | if (g_opt_m) {
2701 | g_runit_1 = "rMbps";
2702 | g_wunit_1 = "wMbps";
2703 | g_runit_2 = "RdMbps";
2704 | g_wunit_2 = "WrMbps";
2705 | }
2706 |
2707 | argv += optind;
2708 | if ((argc - optind) >= 1) {
2709 | interval = atoi(*argv);
2710 | if (interval == 0)
2711 | usage();
2712 | argv++;
2713 | if ((argc - optind) >= 2)
2714 | loop_max = atoi(*argv);
2715 | else
2716 | g_forever = 1;
2717 | }
2718 |
2719 | #ifdef OS_SOLARIS
2720 | /* Open Kstat */
2721 | if ((g_kc = kstat_open()) == NULL)
2722 | die(1, "kstat_open");
2723 | #endif
2724 | if (g_tcp)
2725 | init_tcp();
2726 | if (g_udp)
2727 | init_udp();
2728 | #ifdef OS_SOLARIS
2729 | if ((g_style == STYLE_NONE) && (g_tcp || g_udp))
2730 | if ((! g_tcp_ksp) && (! g_udp_ksp))
2731 | /* Nothing to show */
2732 | exit(1);
2733 | g_tcp = g_tcp && g_tcp_ksp;
2734 | g_udp = g_udp && g_udp_ksp;
2735 | #endif
2736 |
2737 | #ifdef USE_DLADM
2738 | init_dladm();
2739 | #endif
2740 |
2741 | /* Get a socket so I can do ioctl's */
2742 | if ((g_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
2743 | die(1, "socket");
2744 |
2745 | #ifdef OS_SOLARIS
2746 | /* Calculate the period of each iteration */
2747 | period_n = (hrtime_t)interval * NANOSEC;
2748 |
2749 | /* Get time when we started */
2750 | start_n = gethrtime();
2751 | #else /* OS_SOLARIS */
2752 | /* Open the file we got stats from (in Linux) */
2753 | net_dev = open(PROC_NET_DEV_PATH, O_RDONLY, 0);
2754 | if (net_dev < 0)
2755 | die(1, "open: %s", PROC_NET_DEV_PATH);
2756 | if (g_tcp || g_udp) {
2757 | g_snmp = fopen(PROC_NET_SNMP_PATH, "r");
2758 | if (! g_snmp)
2759 | die(1, "fopen: %s", PROC_NET_SNMP_PATH);
2760 | }
2761 | if (g_tcp) {
2762 | g_netstat = fopen(PROC_NET_NETSTAT_PATH, "r");
2763 | if (! g_netstat)
2764 | die(1, "fopen: %s", PROC_NET_NETSTAT_PATH);
2765 | }
2766 |
2767 | /* Get boot-time */
2768 | g_boot_time = fetch_boot_time();
2769 |
2770 | /* Get time when we started */
2771 | if (gettimeofday(&start, (void *) NULL) < 0)
2772 | die(1, "gettimeofday");
2773 | #endif /* OS_SOLARIS */
2774 |
2775 | /*
2776 | * Set up signal handling
2777 | */
2778 | (void) signal(SIGCONT, cont_handler);
2779 |
2780 | if (g_verbose) {
2781 | (void) printf("nicstat version " NICSTAT_VERSION "\n");
2782 | }
2783 |
2784 | /*
2785 | * Main Loop
2786 | */
2787 | for (;;) {
2788 | #if DEBUG > 1
2789 | if (gettimeofday(&debug_now, (void *) NULL) < 0) {
2790 | perror("gettimeofday");
2791 | exit(2);
2792 | }
2793 | fprintf(stderr, " pre-op = %ld.%06ld\n",
2794 | debug_now.tv_sec, debug_now.tv_usec);
2795 | #endif
2796 |
2797 | /*
2798 | * Fetch data and update statistics
2799 | */
2800 | #ifdef OS_SOLARIS
2801 | update_nicdata_list();
2802 | update_stats();
2803 | #else
2804 | update_stats(net_dev);
2805 | #endif
2806 |
2807 | /* Check we matched some NICs */
2808 | if (g_nicdata_count <= 0)
2809 | die(0, "no matching interface");
2810 |
2811 | /*
2812 | * Just a list?
2813 | */
2814 | if (g_list) {
2815 | list_ifs();
2816 | break;
2817 | }
2818 |
2819 | /*
2820 | * Print statistics
2821 | */
2822 | print_stats();
2823 |
2824 | /* end point */
2825 | if (! g_forever)
2826 | if (++loop == loop_max) break;
2827 |
2828 | /* flush output */
2829 | if (fflush(stdout) != 0)
2830 | die(1, "fflush(stdout)");
2831 |
2832 | /*
2833 | * have a kip
2834 | */
2835 | #ifdef OS_SOLARIS
2836 | end_n = gethrtime();
2837 | pause_n = start_n + period_n - end_n;
2838 | time_is_up = pause_n <= 0 || pause_n < (period_n / 4);
2839 | #else /* OS_SOLARIS */
2840 | (void) gettimeofday(&now, NULL);
2841 | start.tv_sec += interval;
2842 | pause_m = (start.tv_sec - now.tv_sec) * 1000;
2843 | pause_m += (start.tv_usec - now.tv_usec) / 1000;
2844 | time_is_up = pause_m <= 0 || pause_m < (interval * 250);
2845 | #endif /* OS_SOLARIS */
2846 | if (time_is_up)
2847 | if (g_forever || g_caught_cont) {
2848 | /* Reset our cadence */
2849 | #ifdef OS_SOLARIS
2850 | start_n = end_n + period_n;
2851 | pause_n = period_n;
2852 | #else /* OS_SOLARIS */
2853 | start.tv_sec = now.tv_sec + interval;
2854 | start.tv_usec = now.tv_usec;
2855 | pause_m = interval * 1000;
2856 | #endif /* OS_SOLARIS */
2857 | } else {
2858 | /*
2859 | * The case for better observability
2860 | *
2861 | * If we got here, then the time
2862 | * between the output we just did, and
2863 | * the scheduled time for the next
2864 | * output is < 1/4 of our requested
2865 | * interval AND the number of
2866 | * intervals has been requested AND we
2867 | * have never caught a SIGCONT (so we
2868 | * have never been suspended). In
2869 | * this case, we'll try to get back to
2870 | * the desired cadence, so we will
2871 | * pause for 1/2 the normal interval
2872 | * this time.
2873 | */
2874 | #ifdef OS_SOLARIS
2875 | pause_n = period_n / 2;
2876 | start_n += period_n;
2877 | #else /* OS_SOLARIS */
2878 | pause_m = interval * 500;
2879 | #endif /* OS_SOLARIS */
2880 | }
2881 | #ifdef OS_SOLARIS
2882 | else
2883 | start_n += period_n;
2884 | if (pause_n > 0)
2885 | sleep_for(pause_n, end_n);
2886 | if ((kc_id = kstat_chain_update(g_kc)) == -1)
2887 | die(1, "kstat_chain_update");
2888 | g_new_kstat_chain = (kc_id != 0);
2889 | #else /* OS_SOLARIS */
2890 | if (pause_m > 0)
2891 | sleep_for(pause_m, &now);
2892 | #endif /* OS_SOLARIS */
2893 | }
2894 |
2895 |
2896 | /*
2897 | * Close Kstat & socket
2898 | */
2899 | #ifdef OS_SOLARIS
2900 | (void) kstat_close(g_kc);
2901 | #endif
2902 | (void) close(g_sock);
2903 |
2904 | return (0);
2905 | }
2906 |
--------------------------------------------------------------------------------
/nicstat.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # nicstat.sh - Wrapper for multi-architecture nicstat install
3 | #
4 | # Tim Cook, 27 Aug 2012
5 |
6 | # Let us see if we can figure out the OS & CPU architecture cheaply
7 |
8 | ostype=unknown
9 | osrel=unknown
10 | cputype=unknown
11 |
12 | OSTYPE_PATH=/proc/sys/kernel/ostype
13 |
14 | if [ -f "/etc/release" ]; then
15 | # Might be Solaris
16 | read f1 f2 f3 rest < /etc/release
17 | case " $f1 $f2 $f3 $rest" in
18 | ' Solaris '[0-9]*' '*' X86' )
19 | ostype=Solaris ; osrel=$f2 ; cputype=i386 ;;
20 | ' Solaris '[0-9]*' '*' SPARC' )
21 | ostype=Solaris ; osrel=$f2 ; cputype=sparc ;;
22 | ' '[A-Z]*' Solaris '[0-9]*' '*' X86' )
23 | ostype=Solaris ; osrel=$f3 ; cputype=i386 ;;
24 | ' '[A-Z]*' Solaris '[0-9]*' '*' SPARC' )
25 | ostype=Solaris ; osrel=$f3 ; cputype=sparc ;;
26 | ' '[A-Z]*' Solaris '[0-9]*.[0-9]*' X86' )
27 | ostype=Solaris ; rel=$f3 ; cputype=i386 ;;
28 | ' '[A-Z]*' Solaris '[0-9]*.[0-9]*' SPARC' )
29 | ostype=Solaris ; rel=$f3 ; cputype=sparc ;;
30 | *' Solaris '* )
31 | ostype=Solaris
32 | cpu=`uname -p`
33 | case "$cpu" in
34 | 'i386' | 'sparc' ) cputype=$cpu ;;
35 | esac
36 | rel=`uname -r`
37 | ;;
38 | esac
39 | case "$rel" in
40 | 5.8 ) osrel=8 ;;
41 | 5.9 ) osrel=9 ;;
42 | 5.10 ) osrel=10 ;;
43 | # NOTE: Treat Solaris releases > 11 as if they were 11
44 | 5.1[1-9] | 1[1-9].* ) osrel=11 ;;
45 | esac
46 | elif [ -f "$OSTYPE_PATH" -a ! -s "$OSTYPE_PATH" ]; then
47 | read f1 rest < "$OSTYPE_PATH"
48 | case " $f1 " in
49 | ' Linux ' ) ostype=Linux ; osclass=Linux ;;
50 | esac
51 | # Let's see if we can refine that
52 | # NOTE: I would want to bunch popular variants that are similar together
53 | # (e.g. Ubuntu and Debian; RHEL & CentOS & OEL)
54 | if [ -f /etc/redhat-release ]; then
55 | ostype=RedHat
56 | read f1 f2 f3 f4 f5 f6 f7 f8 f9 rest < /etc/redhat-release
57 | if [ "$f6" = 'release' ]; then
58 | osrel=$f7
59 | elif [ "$f5" = 'release' ]; then
60 | osrel=$f6
61 | elif [ "$f7" = 'release' ]; then
62 | osrel=$f7
63 | elif [ -f /proc/sys/kernel/osrelease ]; then
64 | read osrel < /proc/sys/kernel/osrelease
65 | fi
66 | case "$osrel" in
67 | *'.'* )
68 | osrel=${osrel%%.*} ;;
69 | esac
70 | elif [ -f /etc/SuSE-release ]; then
71 | ostype=SuSE
72 | while read f1 f2 f3 rest
73 | do
74 | if [ "$f1 $f2" = 'VERSION =' ]; then
75 | osrel=$f3
76 | fi
77 | done < /etc/SuSE-release
78 | elif [ -f /etc/lsb-release ]; then
79 | save_IFS=$IFS
80 | IFS='= '
81 | while read name value
82 | do
83 | case "$name" in
84 | 'DISTRIB_ID' ) ostype=$value ;;
85 | 'DISTRIB_RELEASE' ) osrel=${value%%.*} ;;
86 | esac
87 | done < /etc/lsb-release
88 | IFS=$save_IFS
89 | fi
90 | if [ -z "$ostype" -a -f "/etc/issue" ]; then
91 | read f1 f2 f3 f4 f5 f6 rest < /etc/issue
92 | case "$f1 $f2 $f3 $f4 $f5 $f6 $rest " in
93 | 'Ubuntu [1-9].'* ) ostype=Ubuntu ; osrel=${f2%%.*} ;;
94 | 'Ubuntu [1-9][0-9].'* ) ostype=Ubuntu ; osrel=${f2%%.*} ;;
95 | 'Red Hat Enterprise Server release '[1-9]* )
96 | ostype=RedHat ; osrel=${f6%%.*} ;;
97 | 'Fedora release '[0-9]* ) ostype=Fedora ; osrel=${f3%%.*} ;;
98 | 'Fedora Core release '[0-9]* ) ostype=Fedora ; osrel=${f4%%.*} ;;
99 | esac
100 | fi
101 | fi
102 |
103 | if [ "X$ostype" = "Xunknown" ]; then
104 | os=`uname -sr`
105 | case "$os" in
106 | 'SunOS 5.'* ) ostype=Solaris ;;
107 | 'Linux '* ) ostype=Linux ; osclass=Linux ;;
108 | esac
109 | fi
110 |
111 | if [ "X$cputype" = "Xunknown" ]; then
112 | cpu=`uname -mp`
113 | case "$cpu" in
114 | 'i'[3456]'86 '* ) cputype=i386 ;;
115 | 'i86pc '* ) cputype=i386 ;;
116 | *' sparc' ) cputype=sparc ;;
117 | 'x86_64 '* ) cputype=i386 ;;
118 | esac
119 | fi
120 |
121 | if [ "$ostype,$osrel,$kernrel" = "Linux,," ]; then
122 | # Would be useful to at least get the Linux kernel release
123 | rel=`uname -r`
124 | case "$rel" in
125 | 2.4.* ) kernrel=2.4 ;;
126 | 2.6.* ) kernrel=2.6 ;;
127 | esac
128 | fi
129 |
130 |
131 | #------------------------------------------------------------------------
132 | # MAIN
133 |
134 | # echo "os = [$ostype]"
135 | # echo "cpu = [$cputype]"
136 | # echo "release = [$osrel]"
137 | # exit 0
138 |
139 | SCRIPT_DIR=`dirname "$0"`
140 | BIN_DIR=$SCRIPT_DIR
141 | BIN_NAME=.nicstat
142 |
143 | case "$#,$1" in
144 | '1,--bin-name' )
145 | echo "${BIN_NAME}.${ostype}_${osrel}_${cputype}"
146 | exit 0
147 | ;;
148 | esac
149 |
150 | for s in "${ostype}_${osrel}_${cputype}" \
151 | "${osclass}_${kernrel}_${cputype}" \
152 | "${ostype}_${cputype}" \
153 | "${osclass}_${cputype}"
154 | do
155 | if [ -x "$BIN_DIR/$BIN_NAME.$s" ]; then
156 | BIN="$BIN_DIR/$BIN_NAME.$s"
157 | break
158 | fi
159 | done
160 | if [ "X$BIN" = "X" ]; then
161 | echo "$0: can not find platform executable" >& 2
162 | exit 1
163 | fi
164 |
165 | exec "$BIN" "$@"
166 |
--------------------------------------------------------------------------------