-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodels.py
More file actions
137 lines (114 loc) · 4.32 KB
/
Copy pathmodels.py
File metadata and controls
137 lines (114 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"""Pydantic response models (also drive the OpenAPI schema at /docs).
Peer/dissector model: no roles, no arrows, no IANA services, no local/remote. The
API serves four views -- graph, endpoint, connection protocols, protocol ports --
plus stats, the layer registry, search, and name editing. Direction is surfaced only
as neutral A->B / B->A counters; the human judges direction from the volumes.
"""
from typing import Optional
from pydantic import BaseModel
class Stats(BaseModel):
endpoints: int
connections: int
flows: int
layers: int
total_pkts: int
total_bytes: int
first_seen: Optional[float] = None
last_seen: Optional[float] = None
class Layer(BaseModel):
"""One observed protocol token with its persisted tier + colour and usage count."""
layer: str
tier: str
colour: str
count: int # distinct connections carrying this layer
unresolved: bool = False # True for tshark stop-markers ('data'), not real protocols
encrypted: bool = False # True for the TLS/SSL session boundary (sealed payload, 🔒)
class Node(BaseModel):
ip: str
kind: str # 'unicast' | 'multicast' | 'broadcast'
given_name: Optional[str] = None
whois_name: Optional[str] = None # RDAP-resolved org/netblock handle (ip_whois)
total_pkts: int
total_bytes: int
degree: int
first_seen: Optional[float] = None
last_seen: Optional[float] = None
# Broadcast domain, derived at query time from the `vlans` registry.
category: str = "unassigned"
category_label: str = "Unassigned"
vlan_id: Optional[int] = None
colour: str = "#60a5fa" # broadcast-domain colour from broadcast_domain_colours (fallback = sky-blue)
class Category(BaseModel):
"""One broadcast domain (a VLAN subnet or a fixed bucket) for the filter legend."""
category_key: str
label: str
colour: str
class Edge(BaseModel):
"""An undirected peer edge. layers = the full token set present (client colours/filters)."""
connection_id: int
ip_a: str
ip_b: str
pkts: int
bytes: int
pkts_a2b: int
bytes_a2b: int
pkts_b2a: int
bytes_b2a: int
layers: list[str] = []
flow_count: int = 0 # distinct flows (5-tuples) shared by the pair
first_seen: Optional[float] = None
last_seen: Optional[float] = None
class GraphMeta(BaseModel):
capped: bool
cap: int
shown_endpoints: int
total_endpoints: int
class Graph(BaseModel):
nodes: list[Node]
edges: list[Edge]
meta: Optional[GraphMeta] = None
# Endpoint detail is just a node (which already carries degree).
EndpointDetail = Node
class ConnectionStack(BaseModel):
"""The flows of one connection that share an identical full stack, collapsed.
`layers` is the ordered protocol stack (eth up) common to every flow in the group,
de-noised by the encryption-boundary cut (tokens after an encrypted layer dropped when
its version is known); `flow_count` is how many 5-tuples collapsed into it.
`ports_a`/`ports_b` are the distinct ports each side used across those flows (this is
where port churn lives). `protocol_version` is the decoded version label of the group's
most-specific versioned protocol (TLS/DTLS/QUIC/HTTP/NTP), else null. Direction is
neutral A->B / B->A.
"""
layers: list[str] = []
l4_proto: str
protocol_version: Optional[str] = None
flow_count: int
pkts_a2b: int
bytes_a2b: int
pkts_b2a: int
bytes_b2a: int
ports_a: list[int] = []
ports_b: list[int] = []
first_seen: Optional[float] = None
last_seen: Optional[float] = None
class ConnectionStacks(BaseModel):
"""Edge-click view: the pair summary + its flows grouped by identical stack."""
connection_id: int
ip_a: str
ip_b: str
name_a: Optional[str] = None
name_b: Optional[str] = None
whois_name_a: Optional[str] = None # RDAP-resolved name for ip_a (ip_whois)
whois_name_b: Optional[str] = None # RDAP-resolved name for ip_b
kind_a: str
kind_b: str
# Broadcast-domain colours (from broadcast_domain_colours), so the flow view paints its
# two endpoints the same as the base graph (the single source for endpoint dot colour).
colour_a: str = "#60a5fa"
colour_b: str = "#60a5fa"
pkts_a2b: int
bytes_a2b: int
pkts_b2a: int
bytes_b2a: int
flow_count: int
stacks: list[ConnectionStack]