├── .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 | --------------------------------------------------------------------------------