PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/extras-3.10/pyroute2/netlink/diag/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/extras-3.10/pyroute2/netlink/diag/ss2.py |
#!/usr/bin/env python # pyroute2 - ss2 # Copyright (C) 2018 Matthias Tafelmeier # # ss2 is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; If not, see <http://www.gnu.org/licenses/>. import argparse import json import os import re import socket from socket import AF_INET, AF_UNIX try: import psutil except ImportError: psutil = None from pyroute2.netlink.diag import ( SS_ALL, SS_CLOSE, SS_CLOSE_WAIT, SS_CLOSING, SS_CONN, SS_ESTABLISHED, SS_FIN_WAIT1, SS_FIN_WAIT2, SS_LAST_ACK, SS_LISTEN, SS_SYN_RECV, SS_SYN_SENT, SS_TIME_WAIT, UDIAG_SHOW_NAME, UDIAG_SHOW_PEER, UDIAG_SHOW_VFS, DiagSocket, ) try: from collections.abc import Callable, Mapping except ImportError: from collections import Callable, Mapping # UDIAG_SHOW_ICONS, # UDIAG_SHOW_RQLEN, # UDIAG_SHOW_MEMINFO RUN_AS_MODULE = False class UserCtxtMap(Mapping): _sk_inode_re = re.compile(r"socket:\[(?P<ino>\d+)\]") _proc_sk_fd_cast = "/proc/%d/fd/%d" _BUILD_RECURS_PATH = ["inode", "usr", "pid", "fd"] def _parse_inode(self, sconn): sk_path = self._proc_sk_fd_cast % (sconn.pid, sconn.fd) inode = None sk_inode_raw = os.readlink(sk_path) inode = self._sk_inode_re.search(sk_inode_raw).group("ino") if not inode: raise RuntimeError("Unexpected kernel sk inode outline") return inode def __recurs_enter( self, _sk_inode=None, _sk_fd=None, _usr=None, _pid=None, _ctxt=None, _recurs_path=[], ): step = _recurs_path.pop(0) if self._BUILD_RECURS_PATH[0] == step: if _sk_inode not in self._data.keys(): self._data[_sk_inode] = {} elif self._BUILD_RECURS_PATH[1] == step: if _usr not in self._data[_sk_inode].keys(): self._data[_sk_inode][_usr] = {} elif self._BUILD_RECURS_PATH[2] == step: if _pid not in self._data[_sk_inode][_usr].keys(): self._data[_sk_inode][_usr].__setitem__(_pid, _ctxt) elif self._BUILD_RECURS_PATH[3] == step: self._data[_sk_inode][_usr][_pid]["fds"].append(_sk_fd) # end recursion return else: raise RuntimeError("Unexpected step in recursion") # descend self.__recurs_enter( _sk_inode=_sk_inode, _sk_fd=_sk_fd, _usr=_usr, _pid=_pid, _ctxt=_ctxt, _recurs_path=_recurs_path, ) def _enter_item(self, usr, flow, ctxt): if not flow.pid: # corner case of eg anonnymous AddressFamily.AF_UNIX # sockets return sk_inode = int(self._parse_inode(flow)) sk_fd = flow.fd recurs_path = list(self._BUILD_RECURS_PATH) self.__recurs_enter( _sk_inode=sk_inode, _sk_fd=sk_fd, _usr=usr, _pid=flow.pid, _ctxt=ctxt, _recurs_path=recurs_path, ) def _build(self): for flow in psutil.net_connections(kind="all"): try: proc = psutil.Process(flow.pid) usr = proc.username() ctxt = { "cmd": proc.exe(), "full_cmd": proc.cmdline(), "fds": [], } self._enter_item(usr, flow, ctxt) except (FileNotFoundError, AttributeError, psutil.NoSuchProcess): # Handling edge case of race condition between build and parse # time. That's for very volatile, shortlived flows that can # exist during build but are gone once we want to parse the # inode. pass def __init__(self): self._data = {} self._build() def __getitem__(self, key): return self._data[key] def __len__(self): return len(self._data.keys()) def __delitem__(self, key): raise RuntimeError("Not implemented") def __iter__(self): raise RuntimeError("Not implemented") class Protocol(Callable): class Resolver: @staticmethod def getHost(ip): try: data = socket.gethostbyaddr(ip) host = str(data[0]) return host except Exception: # gracefully return None def __init__(self, sk_states, fmt="json"): self._states = sk_states fmter = "_fmt_%s" % fmt self._fmt = getattr(self, fmter, None) def __call__(self, nl_diag_sk, args, usr_ctxt): raise RuntimeError("not implemented") def _fmt_json(self, refined_stats): return json.dumps(refined_stats, indent=4) class UNIX(Protocol): def __init__(self, sk_states=SS_CONN, _fmt="json"): super(UNIX, self).__init__(sk_states, fmt=_fmt) def __call__(self, nl_diag_sk, args, usr_ctxt): sstats = nl_diag_sk.get_sock_stats( states=self._states, family=AF_UNIX, show=(UDIAG_SHOW_NAME | UDIAG_SHOW_VFS | UDIAG_SHOW_PEER), ) refined_stats = self._refine_diag_raw(sstats, usr_ctxt) return refined_stats def _refine_diag_raw(self, raw_stats, usr_ctxt): refined = {"UNIX": {"flows": []}} def vfs_cb(raw_val): out = {} out["inode"] = raw_val["udiag_vfs_ino"] out["dev"] = raw_val["udiag_vfs_dev"] return out k_idx = 0 val_idx = 1 cb_idx = 1 idiag_attr_refine_map = { "UNIX_DIAG_NAME": ("path_name", None), "UNIX_DIAG_VFS": ("vfs", vfs_cb), "UNIX_DIAG_PEER": ("peer_inode", None), "UNIX_DIAG_SHUTDOWN": ("shutdown", None), } for raw_flow in raw_stats: vessel = {} vessel["inode"] = raw_flow["udiag_ino"] for attr in raw_flow["attrs"]: attr_k = attr[k_idx] attr_val = attr[val_idx] k = idiag_attr_refine_map[attr_k][k_idx] cb = idiag_attr_refine_map[attr_k][cb_idx] if cb: attr_val = cb(attr_val) vessel[k] = attr_val refined["UNIX"]["flows"].append(vessel) if usr_ctxt: for flow in refined["UNIX"]["flows"]: try: sk_inode = flow["inode"] flow["usr_ctxt"] = usr_ctxt[sk_inode] except KeyError: # might define sentinel val pass return refined class TCP(Protocol): INET_DIAG_MEMINFO = 1 INET_DIAG_INFO = 2 INET_DIAG_VEGASINFO = 3 INET_DIAG_CONG = 4 def __init__(self, sk_states=SS_CONN, _fmt="json"): super(TCP, self).__init__(sk_states, fmt=_fmt) IDIAG_EXT_FLAGS = [ self.INET_DIAG_MEMINFO, self.INET_DIAG_INFO, self.INET_DIAG_VEGASINFO, self.INET_DIAG_CONG, ] self.ext_f = 0 for f in IDIAG_EXT_FLAGS: self.ext_f |= 1 << (f - 1) def __call__(self, nl_diag_sk, args, usr_ctxt): sstats = nl_diag_sk.get_sock_stats( states=self._states, family=AF_INET, extensions=self.ext_f ) refined_stats = self._refine_diag_raw(sstats, args.resolve, usr_ctxt) return refined_stats def _refine_diag_raw(self, raw_stats, do_resolve, usr_ctxt): refined = {"TCP": {"flows": []}} idiag_refine_map = { "src": "idiag_src", "dst": "idiag_dst", "src_port": "idiag_sport", "dst_port": "idiag_dport", "inode": "idiag_inode", "iface_idx": "idiag_if", "retrans": "idiag_retrans", } for raw_flow in raw_stats: vessel = {} for k1, k2 in idiag_refine_map.items(): vessel[k1] = raw_flow[k2] for ext_bundle in raw_flow["attrs"]: vessel = self._refine_extension(vessel, ext_bundle) refined["TCP"]["flows"].append(vessel) if usr_ctxt: for flow in refined["TCP"]["flows"]: try: sk_inode = flow["inode"] flow["usr_ctxt"] = usr_ctxt[sk_inode] except KeyError: # might define sentinel val pass if do_resolve: for flow in refined["TCP"]["flows"]: src_host = Protocol.Resolver.getHost(flow["src"]) if src_host: flow["src_host"] = src_host dst_host = Protocol.Resolver.getHost(flow["dst"]) if dst_host: flow["dst_host"] = dst_host return refined def _refine_extension(self, vessel, raw_ext): k, content = raw_ext ext_refine_map = { "meminfo": { "r": "idiag_rmem", "w": "idiag_wmem", "f": "idiag_fmem", "t": "idiag_tmem", } } if k == "INET_DIAG_MEMINFO": mem_k = "meminfo" vessel[mem_k] = {} for k1, k2 in ext_refine_map[mem_k].items(): vessel[mem_k][k1] = content[k2] elif k == "INET_DIAG_CONG": vessel["cong_algo"] = content elif k == "INET_DIAG_INFO": vessel = self._refine_tcp_info(vessel, content) elif k == "INET_DIAG_SHUTDOWN": pass return vessel # interim approach # tcpinfo call backs class InfoCbCore: # normalizer @staticmethod def rto_n_cb(key, value, **ctx): out = None if value != 3000000: out = value / 1000.0 return out @staticmethod def generic_1k_n_cb(key, value, **ctx): return value / 1000.0 # predicates @staticmethod def snd_thresh_p_cb(key, value, **ctx): if value < 0xFFFF: return value return None @staticmethod def rtt_p_cb(key, value, **ctx): tcp_info_raw = ctx["raw"] try: if ( tcp_info_raw["tcpv_enabled"] != 0 and tcp_info_raw["tcpv_rtt"] != 0x7FFFFFFF ): return tcp_info_raw["tcpv_rtt"] except KeyError: # ill practice, yet except quicker path pass return tcp_info_raw["tcpi_rtt"] / 1000.0 # converter @staticmethod def state_c_cb(key, value, **ctx): state_str_map = { SS_ESTABLISHED: "established", SS_SYN_SENT: "syn-sent", SS_SYN_RECV: "syn-recv", SS_FIN_WAIT1: "fin-wait-1", SS_FIN_WAIT2: "fin-wait-2", SS_TIME_WAIT: "time-wait", SS_CLOSE: "unconnected", SS_CLOSE_WAIT: "close-wait", SS_LAST_ACK: "last-ack", SS_LISTEN: "listening", SS_CLOSING: "closing", } return state_str_map[value] @staticmethod def opts_c_cb(key, value, **ctx): tcp_info_raw = ctx["raw"] # tcp_info opt flags TCPI_OPT_TIMESTAMPS = 1 TCPI_OPT_SACK = 2 TCPI_OPT_ECN = 8 out = [] opts = tcp_info_raw["tcpi_options"] if opts & TCPI_OPT_TIMESTAMPS: out.append("ts") if opts & TCPI_OPT_SACK: out.append("sack") if opts & TCPI_OPT_ECN: out.append("ecn") return out def _refine_tcp_info(self, vessel, tcp_info_raw): ti = TCP.InfoCbCore info_refine_tabl = { "tcpi_state": ("state", ti.state_c_cb), "tcpi_pmtu": ("pmtu", None), "tcpi_retrans": ("retrans", None), "tcpi_ato": ("ato", ti.generic_1k_n_cb), "tcpi_rto": ("rto", ti.rto_n_cb), # TODO consider wscale baking "tcpi_snd_wscale": ("snd_wscale", None), "tcpi_rcv_wscale": ("rcv_wscale", None), # TODO bps baking "tcpi_snd_mss": ("snd_mss", None), "tcpi_snd_cwnd": ("snd_cwnd", None), "tcpi_snd_ssthresh": ("snd_ssthresh", ti.snd_thresh_p_cb), # TODO consider rtt agglomeration - needs nesting "tcpi_rtt": ("rtt", ti.rtt_p_cb), "tcpi_rttvar": ("rttvar", ti.generic_1k_n_cb), "tcpi_rcv_rtt": ("rcv_rtt", ti.generic_1k_n_cb), "tcpi_rcv_space": ("rcv_space", None), "tcpi_options": ("opts", ti.opts_c_cb), # unclear, NB not in use by iproute2 ss latest "tcpi_last_data_sent": ("last_data_sent", None), "tcpi_rcv_ssthresh": ("rcv_ssthresh", None), "tcpi_rcv_ssthresh": ("rcv_ssthresh", None), "tcpi_segs_in": ("segs_in", None), "tcpi_segs_out": ("segs_out", None), "tcpi_data_segs_in": ("data_segs_in", None), "tcpi_data_segs_out": ("data_segs_out", None), "tcpi_lost": ("lost", None), "tcpi_notsent_bytes": ("notsent_bytes", None), "tcpi_rcv_mss": ("rcv_mss", None), "tcpi_pacing_rate": ("pacing_rate", None), "tcpi_retransmits": ("retransmits", None), "tcpi_min_rtt": ("min_rtt", None), "tcpi_rwnd_limited": ("rwnd_limited", None), "tcpi_max_pacing_rate": ("max_pacing_rate", None), "tcpi_probes": ("probes", None), "tcpi_reordering": ("reordering", None), "tcpi_last_data_recv": ("last_data_recv", None), "tcpi_bytes_received": ("bytes_received", None), "tcpi_fackets": ("fackets", None), "tcpi_last_ack_recv": ("last_ack_recv", None), "tcpi_last_ack_sent": ("last_ack_sent", None), "tcpi_unacked": ("unacked", None), "tcpi_sacked": ("sacked", None), "tcpi_bytes_acked": ("bytes_acked", None), "tcpi_delivery_rate_app_limited": ( "delivery_rate_app_limited", None, ), "tcpi_delivery_rate": ("delivery_rate", None), "tcpi_sndbuf_limited": ("sndbuf_limited", None), "tcpi_ca_state": ("ca_state", None), "tcpi_busy_time": ("busy_time", None), "tcpi_total_retrans": ("total_retrans", None), "tcpi_advmss": ("advmss", None), "tcpi_backoff": (None, None), "tcpv_enabled": (None, "skip"), "tcpv_rttcnt": (None, "skip"), "tcpv_rtt": (None, "skip"), "tcpv_minrtt": (None, "skip"), # BBR "bbr_bw_lo": ("bbr_bw_lo", None), "bbr_bw_hi": ("bbr_bw_hi", None), "bbr_min_rtt": ("bbr_min_rtt", None), "bbr_pacing_gain": ("bbr_pacing_gain", None), "bbr_cwnd_gain": ("bbr_cwnd_gain", None), # DCTCP "dctcp_enabled": ("dctcp_enabled", None), "dctcp_ce_state": ("dctcp_ce_state", None), "dctcp_alpha": ("dctcp_alpha", None), "dctcp_ab_ecn": ("dctcp_ab_ecn", None), "dctcp_ab_tot": ("dctcp_ab_tot", None), } k_idx = 0 cb_idx = 1 info_k = "tcp_info" vessel[info_k] = {} # BUG - pyroute2 diag - seems always last info instance from kernel if not isinstance(tcp_info_raw, str): for k, v in tcp_info_raw.items(): if k not in info_refine_tabl: continue refined_k = info_refine_tabl[k][k_idx] cb = info_refine_tabl[k][cb_idx] refined_v = v if cb and cb == "skip": continue elif cb: ctx = {"raw": tcp_info_raw} refined_v = cb(k, v, **ctx) vessel[info_k][refined_k] = refined_v return vessel def prepare_args(): parser = argparse.ArgumentParser( description=""" ss2 - socket statistics depictor meant as a complete and convenient surrogate for iproute2/misc/ss2""" ) parser.add_argument( "-x", "--unix", help="Display Unix domain sockets.", action="store_true", ) parser.add_argument( "-t", "--tcp", help="Display TCP sockets.", action="store_true" ) parser.add_argument( "-l", "--listen", help="Display listening sockets.", action="store_true", ) parser.add_argument( "-a", "--all", help="Display all sockets.", action="store_true" ) parser.add_argument( "-p", "--process", help="show socket holding context", action="store_true", ) parser.add_argument( "-r", "--resolve", help="resolve host names in addition", action="store_true", ) args = parser.parse_args() return args def run(args=None): if psutil is None: raise RuntimeError("ss2 requires python-psutil >= 5.0 to run") if not args: args = prepare_args() _states = SS_CONN if args.listen: _states = 1 << SS_LISTEN if args.all: _states = SS_ALL protocols = [] if args.tcp: protocols.append(TCP(sk_states=_states)) if args.unix: protocols.append(UNIX(sk_states=_states)) if not protocols: raise RuntimeError("not implemented - ss2 in fledging mode") _user_ctxt_map = None if args.process: _user_ctxt_map = UserCtxtMap() result = list() with DiagSocket() as ds: ds.bind() for p in protocols: sub_statistics = p(ds, args, _user_ctxt_map) result.append(sub_statistics) if RUN_AS_MODULE: return result else: print(json.dumps(result, indent=4)) if __name__ == "__main__": run()