xref: /xnu-12377.1.9/tools/lldbmacros/ntstat.py (revision f6217f891ac0bb64f3d375211650a4c1ff8ca1ea)
1""" Please make sure you read the README COMPLETELY BEFORE reading anything below.
2    It is very critical that you read coding guidelines in Section E in README file.
3"""
4from xnu import *
5from utils import *
6from string import *
7from socket import *
8from enum import IntEnum
9
10import xnudefines
11from netdefines import *
12from routedefines import *
13
14######################################
15# Globals
16######################################
17""" Refer to in bsd/net/ntstat.c
18"""
19NSTAT_PROCDETAILS_MAGIC = 0xfeedc001
20NSTAT_GENERIC_SHADOW_MAGIC = 0xfadef00d
21TU_SHADOW_MAGIC = 0xfeedf00d
22""" Refer to nstat_provider_type_t in bsd/net/ntstat.h
23"""
24class NSTAT_PROVIDER(IntEnum):
25    NONE = 0
26    ROUTE = 1
27    TCP_KERNEL = 2
28    TCP_USERLAND = 3
29    UDP_KERNEL = 4
30    UDP_USERLAND = 5
31    IFNET = 6
32    SYSINFO = 7
33    QUIC_USERLAND = 8
34    CONN_USERLAND = 9
35    UDP_SUBFLOW = 10
36
37######################################
38# Helper functions
39######################################
40
41def FieldPtrToStructPtr(field_ptr,  field_name, element_type):
42    """ Given a pointer to a field with a structure, return a pointer to the structure itself
43    params:
44        field_ptr       - value : pointer to the field
45        field_name      - str   : string name of the field which holds the list links.
46        element_type    - str   : type of elements to be linked in the list
47    returns:
48        value : A pointer to the start of the structure
49    """
50    out_string = ""
51    if (field_ptr) :
52        tmp_element = Cast(field_ptr, element_type)
53        tmp_element_plus_offset = addressof(tmp_element.__getattr__(field_name))
54        offset = tmp_element_plus_offset - tmp_element
55        original_ptr_as_char_ptr = Cast(field_ptr, 'char *')
56        amended_ptr = original_ptr_as_char_ptr - offset
57        return kern.GetValueFromAddress(unsigned(amended_ptr), element_type)
58    return field_ptr
59
60def ReverseIterateTAILQ_AnonymousHEAD(headval, field_name, element_type):
61    """ reverse iterate over a TAILQ_HEAD in kernel. refer to bsd/sys/queue.h
62    params:
63        headval         - value : value object representing the head of the list
64        field_name      - str   : string name of the field which holds the list links.
65        element_type    - str   : type of elements to be linked in the list
66    returns:
67        A generator does not return. It is used for iterating.
68        value : an object that is of type as headval->tqh_last. Always a pointer object
69    example usage:
70        list_head = kern.GetGlobalVariable('ctl_head')
71        for entry in ReverseIterateTAILQ_AnonymousHEAD(list_head, 'next', 'struct kctl *'):
72            print(entry)
73    """
74    head_first = headval.__getattr__('tqh_first')
75    if head_first:
76        head_first_addr = FieldPtrToStructPtr(addressof(head_first),field_name, element_type)
77        head_last = headval.__getattr__('tqh_last')
78        iter_val = FieldPtrToStructPtr(head_last, field_name, element_type)
79
80        while (unsigned(iter_val) != unsigned(head_first_addr)) and (unsigned(iter_val) != 0) :
81            yield iter_val
82            element = Cast(iter_val, element_type)
83            tmp  = element.__getattr__(field_name).__getattr__('tqe_prev')
84            iter_val = FieldPtrToStructPtr(tmp, field_name, element_type)
85        #end of yield loop
86
87def ShowNstatTUShadow(inshadow):
88    """ Display summary for an nstat_tu_shadow struct
89        params:
90            inshadow : cvalue object which points to 'struct nstat_tu_shadow *'
91    """
92    shad = Cast(inshadow, 'struct nstat_tu_shadow *')
93    procdetails = shad.shad_procdetails
94    out_string = ""
95    if shad :
96        format_string = "nstat_tu_shadow {0: <#0x}: next={1: <#020x} prev={2: <#020x} context (necp_client *)={3: <#020x} live={4: <d}"
97        out_string += format_string.format(shad, shad.shad_link.tqe_next, shad.shad_link.tqe_prev, shad.shad_provider_context, shad.shad_live)
98
99        magic = unsigned(shad.shad_magic)
100        if (magic != TU_SHADOW_MAGIC) :
101            format_string = " INVALID shad magic {0: <#0x}"
102            out_string += format_string.format(magic)
103
104        if (procdetails) :
105            format_string = "  --> procdetails {0: <#0x}: pid={1: <d} name={2: <s} refcnt={3: <d}"
106            out_string += format_string.format(procdetails, procdetails.pdet_pid, procdetails.pdet_procname, procdetails.pdet_refcnt)
107
108            procmagic = unsigned(procdetails.pdet_magic)
109            if (procmagic != NSTAT_PROCDETAILS_MAGIC) :
110                format_string = " INVALID proc magic {0: <#0x}"
111                out_string += format_string.format(procmagic)
112
113    print(out_string)
114
115def ShowNstatGShadow(inshadow):
116    """ Display summary for an nstat_generic_shadow
117        params:
118            inshadow : cvalue object which points to 'struct nstat_generic_shadow *'
119    """
120    gshad = Cast(inshadow, 'struct nstat_generic_shadow *')
121    procdetails = gshad.gshad_procdetails
122    out_string = ""
123    if gshad :
124        prov_string = GetNstatProviderString(gshad.gshad_provider)
125        format_string = "nstat_generic_shadow {0: <#0x}: prov={1: <8s} next={2: <#020x} prev={3: <#020x} refcnt={4: <d} "
126        out_string += format_string.format(gshad, prov_string, gshad.gshad_link.tqe_next, gshad.gshad_link.tqe_prev, gshad.gshad_refcnt)
127
128        ## context
129        if (gshad.gshad_provider == NSTAT_PROVIDER.CONN_USERLAND) :
130            out_string += "context (necp_client *)={0: <#020x} ".format(gshad.gshad_provider_context)
131        elif (gshad.gshad_provider == NSTAT_PROVIDER.UDP_SUBFLOW) :
132            out_string += "context (soflow_hash_entry *)={0: <#020x} ".format(gshad.gshad_provider_context)
133
134        magic = unsigned(gshad.gshad_magic)
135        if (magic != NSTAT_GENERIC_SHADOW_MAGIC) :
136            format_string = " INVALID gshad magic {0: <#0x}"
137            out_string += format_string.format(magic)
138
139        if (procdetails) :
140            format_string = " --> procdetails {0: <#0x}: pid={1: <d} name={2: <s} refcnt={3: <d}"
141            out_string += format_string.format(procdetails, procdetails.pdet_pid, procdetails.pdet_procname, procdetails.pdet_refcnt)
142
143            procmagic = unsigned(procdetails.pdet_magic)
144            if (procmagic != NSTAT_PROCDETAILS_MAGIC) :
145                format_string = " INVALID proc magic {0: <#0x}"
146                out_string += format_string.format(procmagic)
147
148        print(out_string)
149
150        for src in IterateTAILQ_HEAD(gshad.gshad_locus.ntl_src_queue, 'nts_locus_link'):
151            ShowNstatSrc(src)
152    else:
153        print(out_string)
154
155def GetNstatProcdetailsBrief(procdetails):
156    """ Display a brief summary for an nstat_procdetails struct
157        params:
158            procdetails : cvalue object which points to 'struct nstat_procdetails *'
159        returns:
160            str : A string describing various information for the nstat_procdetails structure
161    """
162    procdetails = Cast(procdetails, 'struct nstat_procdetails *')
163    out_string = ""
164    if (procdetails) :
165        format_string = " --> pid={0: <d} name={1: <s} refcnt={2: <d}"
166        out_string += format_string.format(procdetails.pdet_pid, procdetails.pdet_procname, procdetails.pdet_refcnt)
167
168        procmagic = unsigned(procdetails.pdet_magic)
169        if (procmagic != NSTAT_PROCDETAILS_MAGIC) :
170            format_string = " INVALID proc magic {0: <#0x}"
171            out_string += format_string.format(procmagic)
172
173    return out_string
174
175def ShowNstatProcdetails(procdetails):
176    """ Display a summary for an nstat_procdetails struct
177        params:
178            procdetails : cvalue object which points to 'struct nstat_procdetails *'
179    """
180    procdetails = Cast(procdetails, 'struct nstat_procdetails *')
181    out_string = ""
182    if (procdetails) :
183        format_string = "nstat_procdetails: {0: <#020x} next={1: <#020x} prev={2: <#020x} "
184        out_string += format_string.format(procdetails, procdetails.pdet_link.tqe_next, procdetails.pdet_link.tqe_prev)
185        out_string += GetNstatProcdetailsBrief(procdetails)
186
187    print(out_string)
188
189def ShowNstatSockLocus(locus):
190    """ Display a summary for an nstat_sock_locus struct
191        params:
192            locus : cvalue object which points to 'struct nstat_sock_locus *'
193    """
194    locus = Cast(locus, 'struct nstat_sock_locus *')
195    out_string = ""
196    if (locus) :
197        format_string = "nstat_sock_locus: {0: <#020x} next={1: <#020x} prev={2: <#020x}"
198        out_string += format_string.format(locus, locus.nsl_link.tqe_next, locus.nsl_link.tqe_prev)
199        out_string += GetNstatTULocusBrief(locus);
200
201    print(out_string)
202    iterator = IterateTAILQ_HEAD(locus.nsl_locus.ntl_src_queue, 'nts_locus_link')
203    for src in iterator:
204        ShowNstatSrc(src)
205
206
207def GetNstatTUShadowBrief(shadow):
208    """ Display a summary for an nstat_tu_shadow struct
209        params:
210            shadow : cvalue object which points to 'struct nstat_tu_shadow *'
211        returns:
212            str : A string describing various information for the nstat_tu_shadow structure
213    """
214    out_string = ""
215    shad = Cast(shadow, 'struct nstat_tu_shadow *')
216    procdetails = shad.shad_procdetails
217    procdetails = Cast(procdetails, 'struct nstat_procdetails *')
218    out_string = ""
219    if shad :
220        format_string = " shadow {0: <#0x}: necp_client={1: <#020x} live={2: <d}"
221        out_string += format_string.format(shad, shad.shad_provider_context, shad.shad_live)
222        magic = unsigned(shad.shad_magic)
223        if (magic != TU_SHADOW_MAGIC) :
224            format_string = " INVALID shad magic {0: <#0x}"
225            out_string += format_string.format(magic)
226        elif (procdetails) :
227            out_string += GetNstatProcdetailsBrief(procdetails)
228
229    return out_string
230
231def GetNstatGenericShadowBrief(shadow):
232    """ Display a summary for an nstat_generic_shadow struct
233        params:
234            shadow : cvalue object which points to 'struct nstat_generic_shadow *'
235        returns:
236            str : A string describing various information for the nstat_tu_shadow structure
237    """
238    gshad = Cast(shadow, 'struct nstat_generic_shadow *')
239    procdetails = gshad.gshad_procdetails
240    procdetails = Cast(procdetails, 'struct nstat_procdetails *')
241    out_string = ""
242    if gshad :
243        format_string = " gshadow {0: <#0x}:"
244        out_string += format_string.format(gshad)
245        if (gshad.gshad_provider == NSTAT_PROVIDER.CONN_USERLAND) :
246            out_string += "necp_client={0: <#020x} ".format(gshad.gshad_provider_context)
247        elif (gshad.gshad_provider == NSTAT_PROVIDER.UDP_SUBFLOW) :
248            out_string += "soflow_hash_entry={0: <#020x} ".format(gshad.gshad_provider_context)
249        else :
250            out_string += "context {0: <#020x} ".format(gshad.gshad_provider_context)
251        out_string += " refcnt={0: <d} ".format(gshad.gshad_refcnt)
252
253        magic = unsigned(gshad.gshad_magic)
254        if (magic != NSTAT_GENERIC_SHADOW_MAGIC) :
255            format_string = " INVALID gshad magic {0: <#0x}"
256            out_string += format_string.format(magic)
257        elif (procdetails) :
258            out_string += GetNstatProcdetailsBrief(procdetails)
259
260    return out_string
261
262def GetNstatTULocusBrief(cookie):
263    """ Display a summary for an nnstat_sock_locus struct
264        params:
265            cookie : cvalue object which points to 'struct nstat_sock_locus *'
266        returns:
267            str : A string describing various information for the nstat_sock_locus structure
268    """
269    out_string = ""
270    sol = Cast(cookie, 'struct nstat_sock_locus *')
271    inp = sol.nsl_inp
272    inpcb = Cast(inp, 'struct inpcb *')
273    inp_socket = inpcb.inp_socket
274    sock = Cast(inp_socket, 'struct socket *')
275    pname = sol.nsl_pname
276    format_string = " inpcb={0: <#0x}: socket={1: <#020x} process={2: <s}"
277    out_string += format_string.format(inpcb, sock, pname)
278    return out_string
279
280def GetNstatProviderString(provider):
281    providers = {
282        NSTAT_PROVIDER.NONE: "none",
283        NSTAT_PROVIDER.ROUTE: "route",
284        NSTAT_PROVIDER.TCP_KERNEL: "TCP k",
285        NSTAT_PROVIDER.TCP_USERLAND: "TCP u",
286        NSTAT_PROVIDER.UDP_KERNEL: "UDP k",
287        NSTAT_PROVIDER.UDP_USERLAND: "UDP u",
288        NSTAT_PROVIDER.IFNET: "ifnet",
289        NSTAT_PROVIDER.SYSINFO: "sysinfo",
290        NSTAT_PROVIDER.QUIC_USERLAND: "quic u",
291        NSTAT_PROVIDER.CONN_USERLAND: "conn u",
292        NSTAT_PROVIDER.UDP_SUBFLOW: "subflow",
293    }
294    return providers.get(unsigned(provider), "unknown")
295
296def ShowNstatSrc(insrc):
297    """ Display summary for an nstat_src struct
298        params:
299            insrc : cvalue object which points to 'struct nstat_src *'
300    """
301    src = Cast(insrc, 'nstat_src *')
302    prov = src.nts_provider
303    prov = Cast(prov, 'nstat_provider *')
304    prov_string = GetNstatProviderString(prov.nstat_provider_id)
305    out_string = ""
306    if src :
307        format_string = "  nstat_src {0: <#0x}: prov={1: <8s} next={2: <#020x} prev={3: <#020x} srcref={4: <d} seq={5: <d}"
308        out_string += format_string.format(src, prov_string, src.nts_client_link.tqe_next, src.nts_client_link.tqe_prev, src.nts_srcref, src.nts_seq)
309
310        if ((prov.nstat_provider_id == NSTAT_PROVIDER.TCP_USERLAND) or
311            (prov.nstat_provider_id == NSTAT_PROVIDER.UDP_USERLAND) or
312            (prov.nstat_provider_id == NSTAT_PROVIDER.QUIC_USERLAND)) :
313            out_string += GetNstatTUShadowBrief(src.nts_cookie);
314        elif ((prov.nstat_provider_id == NSTAT_PROVIDER.CONN_USERLAND) or
315            (prov.nstat_provider_id == NSTAT_PROVIDER.UDP_SUBFLOW)) :
316            out_string += GetNstatGenericShadowBrief(src.nts_cookie);
317        elif ((prov.nstat_provider_id == NSTAT_PROVIDER.TCP_KERNEL) or
318            (prov.nstat_provider_id == NSTAT_PROVIDER.UDP_KERNEL)) :
319            out_string += GetNstatTULocusBrief(src.nts_cookie);
320
321    print(out_string)
322
323def ShowNstatClient(inclient, reverse):
324    """ Display an nstat_client struct
325        params:
326            client : value object representing an nstat_client in the kernel
327    """
328    client = Cast(inclient, 'nstat_client *')
329    out_string = ""
330    if client :
331        format_string = "\nnstat_client {0: <#0x}: next={1: <#020x} src-head={2: <#020x} tail={3: <#020x}"
332        out_string += format_string.format(client, client.ntc_next, client.ntc_src_queue.tqh_first, client.ntc_src_queue.tqh_last)
333        procdetails = client.ntc_procdetails
334        if (procdetails) :
335            format_string = "  --> procdetails {0: <#0x}: pid={1: <d} name={2: <s} refcnt={3: <d}"
336            out_string += format_string.format(procdetails, procdetails.pdet_pid, procdetails.pdet_procname, procdetails.pdet_refcnt)
337
338    print(out_string)
339    if reverse:
340         print("reverse nstat_src list:")
341         iterator = ReverseIterateTAILQ_AnonymousHEAD(client.ntc_src_queue, 'nts_client_link', 'struct nstat_src *')
342    else:
343         print("nstat_src list:")
344         iterator = IterateTAILQ_HEAD(client.ntc_src_queue, 'nts_client_link')
345    for src in iterator:
346        ShowNstatSrc(src)
347
348######################################
349# Print functions
350######################################
351def PrintNstatClientList(reverse):
352    print("nstat_clients list:")
353    client = kern.globals.nstat_clients
354    client = cast(client, 'nstat_client *')
355    while client != 0:
356        ShowNstatClient(client, reverse)
357        client = cast(client.ntc_next, 'nstat_client *')
358
359def PrintNstatProcdetailList(reverse):
360    procdetails_head = kern.globals.nstat_procdetails_head
361    if reverse:
362        print("\nreverse nstat_procdetails list:\n")
363        iterator = ReverseIterateTAILQ_AnonymousHEAD(procdetails_head, 'pdet_link', 'struct nstat_procdetails *')
364    else:
365        print("\nnstat_procdetails list:\n")
366        iterator = IterateTAILQ_HEAD(procdetails_head, 'pdet_link')
367    for procdetails in iterator:
368        ShowNstatProcdetails(procdetails)
369
370def PrintNstatGenericShadowList(reverse):
371    gshadows = kern.globals.nstat_gshad_head
372    if reverse:
373        print("\nreverse nstat_ghsad list:\n")
374        iterator = ReverseIterateTAILQ_AnonymousHEAD(gshadows, 'gshad_link', 'struct nstat_generic_shadow *')
375    else:
376        print("\nnstat_ghsad list:\n")
377        iterator = IterateTAILQ_HEAD(gshadows, 'gshad_link')
378    for gshad in iterator:
379        ShowNstatGShadow(gshad)
380
381def PrintNstatTUShadowList(reverse):
382    shadows = kern.globals.nstat_userprot_shad_head
383    if reverse:
384        print("\nreverse nstat_userprot_shad list:\n")
385        iterator = ReverseIterateTAILQ_AnonymousHEAD(shadows, 'shad_link', 'struct nstat_tu_shadow *')
386    else:
387        print("\nnstat_userprot_shad list:\n")
388        iterator = IterateTAILQ_HEAD(shadows, 'shad_link')
389    for shad in iterator:
390        ShowNstatTUShadow(shad)
391
392def PrintNstatTCPLocusList(reverse):
393    loci = kern.globals.nstat_tcp_sock_locus_head
394    if reverse:
395        print("\nreverse nstat tcp socket locus list:\n")
396        iterator = ReverseIterateTAILQ_AnonymousHEAD(loci, 'nsl_link', 'struct nstat_sock_locus *')
397    else:
398        print("\nnstat tcp socket locus list:\n")
399        iterator = IterateTAILQ_HEAD(loci, 'nsl_link')
400    for locus in iterator:
401        ShowNstatSockLocus(locus)
402
403def PrintNstatUDPLocusList(reverse):
404    loci = kern.globals.nstat_udp_sock_locus_head
405    if reverse:
406        print("\nreverse nstat udp socket locus list:\n")
407        iterator = ReverseIterateTAILQ_AnonymousHEAD(loci, 'nsl_link', 'struct nstat_sock_locus *')
408    else:
409        print("\nnstat udp socket locus list:\n")
410        iterator = IterateTAILQ_HEAD(loci, 'nsl_link')
411    for locus in iterator:
412        ShowNstatSockLocus(locus)
413
414######################################
415# LLDB commands
416######################################
417# Macro: showallntstat
418
419@lldb_command('showallntstat', 'R')
420def ShowAllNtstat(cmd_args=None, cmd_options={}) :
421    """ Show the contents of various ntstat (network statistics) data structures
422
423        usage: showallntstat [-R]
424            -R  : print ntstat list in reverse
425    """
426    reverse = '-R' in cmd_options
427
428    PrintNstatClientList(reverse)
429    PrintNstatTUShadowList(reverse)
430    PrintNstatGenericShadowList(reverse)
431    PrintNstatProcdetailList(reverse)
432    PrintNstatTCPLocusList(reverse)
433    PrintNstatUDPLocusList(reverse)
434
435# EndMacro: showallntstat
436