1
2
3
4
5 package oshi.software.os.mac;
6
7 import static com.sun.jna.platform.mac.SystemB.INT_SIZE;
8 import static com.sun.jna.platform.mac.SystemB.PROC_ALL_PIDS;
9 import static oshi.jna.platform.mac.SystemB.AF_INET;
10 import static oshi.jna.platform.mac.SystemB.AF_INET6;
11 import static oshi.jna.platform.mac.SystemB.PROC_PIDFDSOCKETINFO;
12 import static oshi.jna.platform.mac.SystemB.PROC_PIDLISTFDS;
13 import static oshi.jna.platform.mac.SystemB.PROX_FDTYPE_SOCKET;
14 import static oshi.jna.platform.mac.SystemB.SOCKINFO_IN;
15 import static oshi.jna.platform.mac.SystemB.SOCKINFO_TCP;
16 import static oshi.software.os.InternetProtocolStats.TcpState.CLOSED;
17 import static oshi.software.os.InternetProtocolStats.TcpState.CLOSE_WAIT;
18 import static oshi.software.os.InternetProtocolStats.TcpState.CLOSING;
19 import static oshi.software.os.InternetProtocolStats.TcpState.ESTABLISHED;
20 import static oshi.software.os.InternetProtocolStats.TcpState.FIN_WAIT_1;
21 import static oshi.software.os.InternetProtocolStats.TcpState.FIN_WAIT_2;
22 import static oshi.software.os.InternetProtocolStats.TcpState.LAST_ACK;
23 import static oshi.software.os.InternetProtocolStats.TcpState.LISTEN;
24 import static oshi.software.os.InternetProtocolStats.TcpState.NONE;
25 import static oshi.software.os.InternetProtocolStats.TcpState.SYN_RECV;
26 import static oshi.software.os.InternetProtocolStats.TcpState.SYN_SENT;
27 import static oshi.software.os.InternetProtocolStats.TcpState.TIME_WAIT;
28 import static oshi.software.os.InternetProtocolStats.TcpState.UNKNOWN;
29 import static oshi.util.Memoizer.defaultExpiration;
30 import static oshi.util.Memoizer.memoize;
31
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.function.Supplier;
35
36 import com.sun.jna.Memory;
37
38 import oshi.annotation.concurrent.ThreadSafe;
39 import oshi.driver.unix.NetStat;
40 import oshi.jna.platform.mac.SystemB;
41 import oshi.jna.platform.mac.SystemB.InSockInfo;
42 import oshi.jna.platform.mac.SystemB.ProcFdInfo;
43 import oshi.jna.platform.mac.SystemB.SocketFdInfo;
44 import oshi.jna.platform.unix.CLibrary.BsdIp6stat;
45 import oshi.jna.platform.unix.CLibrary.BsdIpstat;
46 import oshi.jna.platform.unix.CLibrary.BsdTcpstat;
47 import oshi.jna.platform.unix.CLibrary.BsdUdpstat;
48 import oshi.software.common.AbstractInternetProtocolStats;
49 import oshi.util.ParseUtil;
50 import oshi.util.platform.mac.SysctlUtil;
51 import oshi.util.tuples.Pair;
52
53
54
55
56 @ThreadSafe
57 public class MacInternetProtocolStats extends AbstractInternetProtocolStats {
58
59 private boolean isElevated;
60
61 public MacInternetProtocolStats(boolean elevated) {
62 this.isElevated = elevated;
63 }
64
65 private Supplier<Pair<Long, Long>> establishedv4v6 = memoize(NetStat::queryTcpnetstat, defaultExpiration());
66 private Supplier<BsdTcpstat> tcpstat = memoize(MacInternetProtocolStats::queryTcpstat, defaultExpiration());
67 private Supplier<BsdUdpstat> udpstat = memoize(MacInternetProtocolStats::queryUdpstat, defaultExpiration());
68
69
70 private Supplier<BsdIpstat> ipstat = memoize(MacInternetProtocolStats::queryIpstat, defaultExpiration());
71 private Supplier<BsdIp6stat> ip6stat = memoize(MacInternetProtocolStats::queryIp6stat, defaultExpiration());
72
73 @Override
74 public TcpStats getTCPv4Stats() {
75 BsdTcpstat tcp = tcpstat.get();
76 if (this.isElevated) {
77 return new TcpStats(establishedv4v6.get().getA(), ParseUtil.unsignedIntToLong(tcp.tcps_connattempt),
78 ParseUtil.unsignedIntToLong(tcp.tcps_accepts), ParseUtil.unsignedIntToLong(tcp.tcps_conndrops),
79 ParseUtil.unsignedIntToLong(tcp.tcps_drops), ParseUtil.unsignedIntToLong(tcp.tcps_sndpack),
80 ParseUtil.unsignedIntToLong(tcp.tcps_rcvpack), ParseUtil.unsignedIntToLong(tcp.tcps_sndrexmitpack),
81 ParseUtil.unsignedIntToLong(
82 tcp.tcps_rcvbadsum + tcp.tcps_rcvbadoff + tcp.tcps_rcvmemdrop + tcp.tcps_rcvshort),
83 0L);
84 }
85 BsdIpstat ip = ipstat.get();
86 BsdUdpstat udp = udpstat.get();
87 return new TcpStats(establishedv4v6.get().getA(), ParseUtil.unsignedIntToLong(tcp.tcps_connattempt),
88 ParseUtil.unsignedIntToLong(tcp.tcps_accepts), ParseUtil.unsignedIntToLong(tcp.tcps_conndrops),
89 ParseUtil.unsignedIntToLong(tcp.tcps_drops),
90 Math.max(0L, ParseUtil.unsignedIntToLong(ip.ips_delivered - udp.udps_opackets)),
91 Math.max(0L, ParseUtil.unsignedIntToLong(ip.ips_total - udp.udps_ipackets)),
92 ParseUtil.unsignedIntToLong(tcp.tcps_sndrexmitpack),
93 Math.max(0L, ParseUtil.unsignedIntToLong(ip.ips_badsum + ip.ips_tooshort + ip.ips_toosmall
94 + ip.ips_badhlen + ip.ips_badlen - udp.udps_hdrops + udp.udps_badsum + udp.udps_badlen)),
95 0L);
96 }
97
98 @Override
99 public TcpStats getTCPv6Stats() {
100 BsdIp6stat ip6 = ip6stat.get();
101 BsdUdpstat udp = udpstat.get();
102 return new TcpStats(establishedv4v6.get().getB(), 0L, 0L, 0L, 0L,
103 ip6.ip6s_localout - ParseUtil.unsignedIntToLong(udp.udps_snd6_swcsum),
104 ip6.ip6s_total - ParseUtil.unsignedIntToLong(udp.udps_rcv6_swcsum), 0L, 0L, 0L);
105 }
106
107 @Override
108 public UdpStats getUDPv4Stats() {
109 BsdUdpstat stat = udpstat.get();
110 return new UdpStats(ParseUtil.unsignedIntToLong(stat.udps_opackets),
111 ParseUtil.unsignedIntToLong(stat.udps_ipackets), ParseUtil.unsignedIntToLong(stat.udps_noportmcast),
112 ParseUtil.unsignedIntToLong(stat.udps_hdrops + stat.udps_badsum + stat.udps_badlen));
113 }
114
115 @Override
116 public UdpStats getUDPv6Stats() {
117 BsdUdpstat stat = udpstat.get();
118 return new UdpStats(ParseUtil.unsignedIntToLong(stat.udps_snd6_swcsum),
119 ParseUtil.unsignedIntToLong(stat.udps_rcv6_swcsum), 0L, 0L);
120 }
121
122 @Override
123 public List<IPConnection> getConnections() {
124 List<IPConnection> conns = new ArrayList<>();
125 int[] pids = new int[1024];
126 int numberOfProcesses = SystemB.INSTANCE.proc_listpids(PROC_ALL_PIDS, 0, pids, pids.length * INT_SIZE)
127 / INT_SIZE;
128 for (int i = 0; i < numberOfProcesses; i++) {
129
130
131 if (pids[i] > 0) {
132 for (Integer fd : queryFdList(pids[i])) {
133 IPConnection ipc = queryIPConnection(pids[i], fd);
134 if (ipc != null) {
135 conns.add(ipc);
136 }
137 }
138 }
139 }
140 return conns;
141 }
142
143 private static List<Integer> queryFdList(int pid) {
144 List<Integer> fdList = new ArrayList<>();
145 int bufferSize = SystemB.INSTANCE.proc_pidinfo(pid, PROC_PIDLISTFDS, 0, null, 0);
146 if (bufferSize > 0) {
147 ProcFdInfo fdInfo = new ProcFdInfo();
148 int numStructs = bufferSize / fdInfo.size();
149 ProcFdInfo[] fdArray = (ProcFdInfo[]) fdInfo.toArray(numStructs);
150 bufferSize = SystemB.INSTANCE.proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdArray[0], bufferSize);
151 numStructs = bufferSize / fdInfo.size();
152 for (int i = 0; i < numStructs; i++) {
153 if (fdArray[i].proc_fdtype == PROX_FDTYPE_SOCKET) {
154 fdList.add(fdArray[i].proc_fd);
155 }
156 }
157 }
158 return fdList;
159 }
160
161 private static IPConnection queryIPConnection(int pid, int fd) {
162 try (SocketFdInfo si = new SocketFdInfo()) {
163 int ret = SystemB.INSTANCE.proc_pidfdinfo(pid, fd, PROC_PIDFDSOCKETINFO, si, si.size());
164 if (si.size() == ret && si.psi.soi_family == AF_INET || si.psi.soi_family == AF_INET6) {
165 InSockInfo ini;
166 String type;
167 TcpState state;
168 if (si.psi.soi_kind == SOCKINFO_TCP) {
169 si.psi.soi_proto.setType("pri_tcp");
170 si.psi.soi_proto.read();
171 ini = si.psi.soi_proto.pri_tcp.tcpsi_ini;
172 state = stateLookup(si.psi.soi_proto.pri_tcp.tcpsi_state);
173 type = "tcp";
174 } else if (si.psi.soi_kind == SOCKINFO_IN) {
175 si.psi.soi_proto.setType("pri_in");
176 si.psi.soi_proto.read();
177 ini = si.psi.soi_proto.pri_in;
178 state = NONE;
179 type = "udp";
180 } else {
181 return null;
182 }
183
184 byte[] laddr;
185 byte[] faddr;
186 if (ini.insi_vflag == 1) {
187 laddr = ParseUtil.parseIntToIP(ini.insi_laddr[3]);
188 faddr = ParseUtil.parseIntToIP(ini.insi_faddr[3]);
189 type += "4";
190 } else if (ini.insi_vflag == 2) {
191 laddr = ParseUtil.parseIntArrayToIP(ini.insi_laddr);
192 faddr = ParseUtil.parseIntArrayToIP(ini.insi_faddr);
193 type += "6";
194 } else if (ini.insi_vflag == 3) {
195 laddr = ParseUtil.parseIntToIP(ini.insi_laddr[3]);
196 faddr = ParseUtil.parseIntToIP(ini.insi_faddr[3]);
197 type += "46";
198 } else {
199 return null;
200 }
201 int lport = ParseUtil.bigEndian16ToLittleEndian(ini.insi_lport);
202 int fport = ParseUtil.bigEndian16ToLittleEndian(ini.insi_fport);
203 return new IPConnection(type, laddr, lport, faddr, fport, state, si.psi.soi_qlen, si.psi.soi_incqlen,
204 pid);
205 }
206 }
207 return null;
208 }
209
210 private static TcpState stateLookup(int state) {
211 switch (state) {
212 case 0:
213 return CLOSED;
214 case 1:
215 return LISTEN;
216 case 2:
217 return SYN_SENT;
218 case 3:
219 return SYN_RECV;
220 case 4:
221 return ESTABLISHED;
222 case 5:
223 return CLOSE_WAIT;
224 case 6:
225 return FIN_WAIT_1;
226 case 7:
227 return CLOSING;
228 case 8:
229 return LAST_ACK;
230 case 9:
231 return FIN_WAIT_2;
232 case 10:
233 return TIME_WAIT;
234 default:
235 return UNKNOWN;
236 }
237 }
238
239
240
241
242
243
244
245 private static BsdTcpstat queryTcpstat() {
246 BsdTcpstat mt = new BsdTcpstat();
247 try (Memory m = SysctlUtil.sysctl("net.inet.tcp.stats")) {
248 if (m != null && m.size() >= 128) {
249 mt.tcps_connattempt = m.getInt(0);
250 mt.tcps_accepts = m.getInt(4);
251 mt.tcps_drops = m.getInt(12);
252 mt.tcps_conndrops = m.getInt(16);
253 mt.tcps_sndpack = m.getInt(64);
254 mt.tcps_sndrexmitpack = m.getInt(72);
255 mt.tcps_rcvpack = m.getInt(104);
256 mt.tcps_rcvbadsum = m.getInt(112);
257 mt.tcps_rcvbadoff = m.getInt(116);
258 mt.tcps_rcvmemdrop = m.getInt(120);
259 mt.tcps_rcvshort = m.getInt(124);
260 }
261 }
262 return mt;
263 }
264
265 private static BsdIpstat queryIpstat() {
266 BsdIpstat mi = new BsdIpstat();
267 try (Memory m = SysctlUtil.sysctl("net.inet.ip.stats")) {
268 if (m != null && m.size() >= 60) {
269 mi.ips_total = m.getInt(0);
270 mi.ips_badsum = m.getInt(4);
271 mi.ips_tooshort = m.getInt(8);
272 mi.ips_toosmall = m.getInt(12);
273 mi.ips_badhlen = m.getInt(16);
274 mi.ips_badlen = m.getInt(20);
275 mi.ips_delivered = m.getInt(56);
276 }
277 }
278 return mi;
279 }
280
281 private static BsdIp6stat queryIp6stat() {
282 BsdIp6stat mi6 = new BsdIp6stat();
283 try (Memory m = SysctlUtil.sysctl("net.inet6.ip6.stats")) {
284 if (m != null && m.size() >= 96) {
285 mi6.ip6s_total = m.getLong(0);
286 mi6.ip6s_localout = m.getLong(88);
287 }
288 }
289 return mi6;
290 }
291
292 public static BsdUdpstat queryUdpstat() {
293 BsdUdpstat ut = new BsdUdpstat();
294 try (Memory m = SysctlUtil.sysctl("net.inet.udp.stats")) {
295 if (m != null && m.size() >= 1644) {
296 ut.udps_ipackets = m.getInt(0);
297 ut.udps_hdrops = m.getInt(4);
298 ut.udps_badsum = m.getInt(8);
299 ut.udps_badlen = m.getInt(12);
300 ut.udps_opackets = m.getInt(36);
301 ut.udps_noportmcast = m.getInt(48);
302 ut.udps_rcv6_swcsum = m.getInt(64);
303 ut.udps_snd6_swcsum = m.getInt(80);
304 }
305 }
306 return ut;
307 }
308 }