traffic-taffy 0.2__py3-none-any.whl → 0.3.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pcap_compare/__init__.py +0 -0
- pcap_compare/cache_info.py +46 -0
- pcap_compare/compare.py +288 -0
- pcap_compare/dissectmany.py +21 -0
- pcap_compare/dissector.py +512 -0
- pcap_compare/dissectorresults.py +21 -0
- pcap_compare/graph.py +210 -0
- traffic_taffy/cache_info.py +6 -0
- traffic_taffy/compare.py +106 -35
- traffic_taffy/dissectmany.py +1 -1
- traffic_taffy/dissector.py +60 -5
- traffic_taffy/explore.py +222 -0
- traffic_taffy/graph.py +0 -16
- traffic_taffy/pcap_splitter.py +36 -17
- {traffic_taffy-0.2.dist-info → traffic_taffy-0.3.5.dist-info}/METADATA +6 -1
- traffic_taffy-0.3.5.dist-info/RECORD +21 -0
- traffic_taffy-0.2.dist-info/RECORD +0 -13
- {traffic_taffy-0.2.dist-info → traffic_taffy-0.3.5.dist-info}/WHEEL +0 -0
- {traffic_taffy-0.2.dist-info → traffic_taffy-0.3.5.dist-info}/entry_points.txt +0 -0
- {traffic_taffy-0.2.dist-info → traffic_taffy-0.3.5.dist-info}/top_level.txt +0 -0
traffic_taffy/explore.py
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
import sys
|
2
|
+
import logging
|
3
|
+
from logging import info, debug
|
4
|
+
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
|
5
|
+
from PyQt6.QtWidgets import QPushButton, QVBoxLayout, QLineEdit, QTextEdit, QWidget, QApplication, QLabel
|
6
|
+
from traffic_taffy.dissector import (
|
7
|
+
PCAPDissectorType,
|
8
|
+
dissector_add_parseargs,
|
9
|
+
limitor_add_parseargs,
|
10
|
+
PCAPDissector,
|
11
|
+
check_dissector_level,
|
12
|
+
)
|
13
|
+
from traffic_taffy.compare import PcapCompare
|
14
|
+
|
15
|
+
# https://stackoverflow.com/questions/32476006/how-to-make-an-expandable-collapsable-section-widget-in-qt
|
16
|
+
|
17
|
+
# class Widget(QWidget):
|
18
|
+
# def __init__(self):
|
19
|
+
# super().__init__()
|
20
|
+
# self.__initUi()
|
21
|
+
|
22
|
+
# def __initUi(self):
|
23
|
+
# addBtn = QPushButton('Add')
|
24
|
+
# addBtn.clicked.connect(self.__add)
|
25
|
+
# self.__foldableListWidget = FoldableListWidget()
|
26
|
+
# lay = QVBoxLayout()
|
27
|
+
# lay.addWidget(addBtn)
|
28
|
+
# lay.addWidget(self.__foldableListWidget)
|
29
|
+
# self.setLayout(lay)
|
30
|
+
|
31
|
+
# def __add(self):
|
32
|
+
# foldedItem = QLabel("folded")
|
33
|
+
# # foldedItem.setPlaceholderText('Input...')
|
34
|
+
|
35
|
+
# sublist = FoldableListWidget()
|
36
|
+
# subitem1 = QLabel("main item")
|
37
|
+
# subitem2 = QLabel("sub item")
|
38
|
+
# sublist.setFoldableListWidgetItem(subitem1, subitem2)
|
39
|
+
|
40
|
+
# self.__foldableListWidget.setFoldableListWidgetItem(foldedItem, sublist)
|
41
|
+
|
42
|
+
|
43
|
+
from PyQt6.QtWidgets import (QPushButton, QDialog, QTreeWidget,
|
44
|
+
QTreeWidgetItem, QVBoxLayout,
|
45
|
+
QHBoxLayout, QFrame, QLabel,
|
46
|
+
QApplication)
|
47
|
+
|
48
|
+
class SectionExpandButton(QPushButton):
|
49
|
+
"""a QPushbutton that can expand or collapse its section
|
50
|
+
"""
|
51
|
+
def __init__(self, item, text = "", parent = None):
|
52
|
+
super().__init__(text, parent)
|
53
|
+
self.section = item
|
54
|
+
self.clicked.connect(self.on_clicked)
|
55
|
+
|
56
|
+
def on_clicked(self):
|
57
|
+
"""toggle expand/collapse of section by clicking
|
58
|
+
"""
|
59
|
+
if self.section.isExpanded():
|
60
|
+
self.section.setExpanded(False)
|
61
|
+
else:
|
62
|
+
self.section.setExpanded(True)
|
63
|
+
|
64
|
+
|
65
|
+
class TaffyExplorer(QDialog):
|
66
|
+
"""Explore PCAP files by comparison slices"""
|
67
|
+
def __init__(self, args):
|
68
|
+
super().__init__()
|
69
|
+
self.tree = QTreeWidget()
|
70
|
+
self.tree.setHeaderHidden(True)
|
71
|
+
self.mainLayout = QVBoxLayout()
|
72
|
+
self.mainLayout.addWidget(self.tree)
|
73
|
+
self.setLayout(self.mainLayout)
|
74
|
+
self.tree.setIndentation(0)
|
75
|
+
|
76
|
+
self.sections = []
|
77
|
+
self.define_sections()
|
78
|
+
self.add_sections()
|
79
|
+
|
80
|
+
self.plusone = QPushButton("Add one")
|
81
|
+
self.mainLayout.addWidget(self.plusone)
|
82
|
+
self.plusone.clicked.connect(self.addone)
|
83
|
+
|
84
|
+
self.args = args
|
85
|
+
|
86
|
+
def addone(self):
|
87
|
+
print("here")
|
88
|
+
self.add_section("new item", QLabel("one thing"))
|
89
|
+
|
90
|
+
def add_section(self, title, widget):
|
91
|
+
button1 = self.add_button(title)
|
92
|
+
section1 = self.add_widget(button1, widget)
|
93
|
+
button1.addChild(section1)
|
94
|
+
|
95
|
+
def add_sections(self):
|
96
|
+
"""adds a collapsible sections for every
|
97
|
+
(title, widget) tuple in self.sections
|
98
|
+
"""
|
99
|
+
#self.tree.clear()
|
100
|
+
for (title, widget) in self.sections:
|
101
|
+
self.add_section(title, widget)
|
102
|
+
|
103
|
+
def define_sections(self):
|
104
|
+
"""reimplement this to define all your sections
|
105
|
+
and add them as (title, widget) tuples to self.sections
|
106
|
+
"""
|
107
|
+
widget = QFrame(self.tree)
|
108
|
+
layout = QHBoxLayout(widget)
|
109
|
+
layout.addWidget(QLabel("Bla"))
|
110
|
+
layout.addWidget(QLabel("Blubb"))
|
111
|
+
title = "Section 1"
|
112
|
+
self.sections.append((title, widget))
|
113
|
+
|
114
|
+
def add_button(self, title):
|
115
|
+
"""creates a QTreeWidgetItem containing a button
|
116
|
+
to expand or collapse its section
|
117
|
+
"""
|
118
|
+
item = QTreeWidgetItem()
|
119
|
+
self.tree.addTopLevelItem(item)
|
120
|
+
self.tree.setItemWidget(item, 0, SectionExpandButton(item, text = title))
|
121
|
+
return item
|
122
|
+
|
123
|
+
def add_widget(self, button, widget):
|
124
|
+
"""creates a QWidgetItem containing the widget,
|
125
|
+
as child of the button-QWidgetItem
|
126
|
+
"""
|
127
|
+
section = QTreeWidgetItem(button)
|
128
|
+
section.setDisabled(True)
|
129
|
+
self.tree.setItemWidget(section, 0, widget)
|
130
|
+
return section
|
131
|
+
|
132
|
+
def create_comparison(self):
|
133
|
+
self.pc = PcapCompare(
|
134
|
+
self.args.pcap_files,
|
135
|
+
maximum_count=self.args.packet_count,
|
136
|
+
print_threshold=float(self.args.print_threshold) / 100.0,
|
137
|
+
print_minimum_count=self.args.minimum_count,
|
138
|
+
print_match_string=self.args.match_string,
|
139
|
+
only_positive=self.args.only_positive,
|
140
|
+
only_negative=self.args.only_negative,
|
141
|
+
cache_results=self.args.cache_pcap_results,
|
142
|
+
dissection_level=self.args.dissection_level,
|
143
|
+
between_times=self.args.between_times,
|
144
|
+
)
|
145
|
+
|
146
|
+
# compare the pcaps
|
147
|
+
self.pcap_data = list(self.pc.load_pcaps())
|
148
|
+
|
149
|
+
def show_comparison(self, pcap_one, timestamp_one, pcap_two, timestamp_two):
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
def parse_args():
|
154
|
+
"Parse the command line arguments."
|
155
|
+
parser = ArgumentParser(
|
156
|
+
formatter_class=ArgumentDefaultsHelpFormatter,
|
157
|
+
description=__doc__,
|
158
|
+
epilog="Exmaple Usage: ",
|
159
|
+
)
|
160
|
+
|
161
|
+
limiting_parser = limitor_add_parseargs(parser)
|
162
|
+
|
163
|
+
limiting_parser.add_argument(
|
164
|
+
"-t",
|
165
|
+
"--print-threshold",
|
166
|
+
default=0.0,
|
167
|
+
type=float,
|
168
|
+
help="Don't print results with abs(percent) less than this threshold",
|
169
|
+
)
|
170
|
+
|
171
|
+
limiting_parser.add_argument(
|
172
|
+
"-P", "--only-positive", action="store_true", help="Only show positive entries"
|
173
|
+
)
|
174
|
+
|
175
|
+
limiting_parser.add_argument(
|
176
|
+
"-N", "--only-negative", action="store_true", help="Only show negative entries"
|
177
|
+
)
|
178
|
+
|
179
|
+
limiting_parser.add_argument(
|
180
|
+
"-T",
|
181
|
+
"--between-times",
|
182
|
+
nargs=2,
|
183
|
+
type=int,
|
184
|
+
help="For single files, only display results between these timestamps",
|
185
|
+
)
|
186
|
+
|
187
|
+
dissector_add_parseargs(parser)
|
188
|
+
|
189
|
+
debugging_group = parser.add_argument_group("Debugging options")
|
190
|
+
|
191
|
+
debugging_group.add_argument(
|
192
|
+
"--log-level",
|
193
|
+
"--ll",
|
194
|
+
default="info",
|
195
|
+
help="Define the logging verbosity level (debug, info, warning, error, ...).",
|
196
|
+
)
|
197
|
+
|
198
|
+
parser.add_argument("pcap_files", type=str, nargs="*", help="PCAP files to analyze")
|
199
|
+
|
200
|
+
args = parser.parse_args()
|
201
|
+
log_level = args.log_level.upper()
|
202
|
+
logging.basicConfig(level=log_level, format="%(levelname)-10s:\t%(message)s")
|
203
|
+
|
204
|
+
check_dissector_level(args.dissection_level)
|
205
|
+
|
206
|
+
return args
|
207
|
+
|
208
|
+
|
209
|
+
def main():
|
210
|
+
args = parse_args()
|
211
|
+
|
212
|
+
app = QApplication(sys.argv)
|
213
|
+
window = TaffyExplorer(args)
|
214
|
+
window.create_comparison()
|
215
|
+
window.show()
|
216
|
+
sys.exit(app.exec())
|
217
|
+
|
218
|
+
|
219
|
+
|
220
|
+
if __name__ == "__main__":
|
221
|
+
main()
|
222
|
+
|
traffic_taffy/graph.py
CHANGED
@@ -26,14 +26,6 @@ def parse_args():
|
|
26
26
|
epilog="Exmaple Usage: ",
|
27
27
|
)
|
28
28
|
|
29
|
-
parser.add_argument(
|
30
|
-
"-g",
|
31
|
-
"--graph-elements",
|
32
|
-
default=None,
|
33
|
-
type=str,
|
34
|
-
help="Graph these particular elements; the default is packet counts",
|
35
|
-
)
|
36
|
-
|
37
29
|
parser.add_argument(
|
38
30
|
"-o",
|
39
31
|
"--output-file",
|
@@ -49,14 +41,6 @@ def parse_args():
|
|
49
41
|
help="Define verbosity level (debug, info, warning, error, fotal, critical).",
|
50
42
|
)
|
51
43
|
|
52
|
-
parser.add_argument(
|
53
|
-
"-b",
|
54
|
-
"--bin-size",
|
55
|
-
type=int,
|
56
|
-
default=1,
|
57
|
-
help="Bin results into this many seconds",
|
58
|
-
)
|
59
|
-
|
60
44
|
parser.add_argument(
|
61
45
|
"-i",
|
62
46
|
"--interactive",
|
traffic_taffy/pcap_splitter.py
CHANGED
@@ -3,10 +3,11 @@
|
|
3
3
|
import io
|
4
4
|
import os
|
5
5
|
import multiprocessing
|
6
|
+
from traffic_taffy.dissector import PCAPDissector
|
6
7
|
from typing import List
|
7
8
|
import dpkt
|
8
9
|
from concurrent.futures import ProcessPoolExecutor, Future
|
9
|
-
from logging import debug
|
10
|
+
from logging import debug, info
|
10
11
|
|
11
12
|
|
12
13
|
class PCAPSplitter:
|
@@ -26,6 +27,7 @@ class PCAPSplitter:
|
|
26
27
|
self.split_size: int = split_size
|
27
28
|
self.maximum_count: int = maximum_count
|
28
29
|
self.pcap_filter: str | None = pcap_filter
|
30
|
+
self.maximum_cores = maximum_cores
|
29
31
|
|
30
32
|
self.header: bytes = None
|
31
33
|
self.buffer: bytes = None
|
@@ -38,29 +40,46 @@ class PCAPSplitter:
|
|
38
40
|
if not os.path.exists(self.pcap_file):
|
39
41
|
raise ValueError(f"failed to find pcap file '{self.pcap_file}'")
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
def set_split_size(self):
|
44
|
+
"Attempt to calculate a reasonable split size"
|
45
|
+
if self.split_size:
|
46
|
+
info(f"split size already set to {self.split_size}")
|
47
|
+
return self.split_size
|
45
48
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
+
cores = multiprocessing.cpu_count()
|
50
|
+
if self.maximum_cores and cores > self.maximum_cores:
|
51
|
+
cores = self.maximum_cores
|
52
|
+
|
53
|
+
if self.maximum_count and self.maximum_count > 0:
|
54
|
+
# not ideal math, but better than nothing
|
55
|
+
self.split_size = int(self.maximum_count / cores)
|
56
|
+
else:
|
57
|
+
if isinstance(self.our_data, io.BufferedReader):
|
58
|
+
# raw uncompressed file
|
59
|
+
divide_size = 1200
|
49
60
|
else:
|
50
|
-
#
|
51
|
-
|
52
|
-
|
53
|
-
|
61
|
+
# likely a compressed file
|
62
|
+
divide_size = 5000
|
63
|
+
|
64
|
+
# even worse math and assumes generally large packets
|
65
|
+
stats = os.stat(self.pcap_file)
|
66
|
+
file_size = stats.st_size
|
67
|
+
self.split_size = int(file_size / divide_size / cores)
|
68
|
+
debug(
|
69
|
+
f"split info: {file_size=}, {divide_size=}, {cores=}, {self.split_size=}"
|
70
|
+
)
|
54
71
|
|
55
|
-
|
56
|
-
|
57
|
-
|
72
|
+
# even 1000 is kinda silly to split, but is better than nothing
|
73
|
+
self.split_size = max(self.split_size, 1000)
|
74
|
+
debug(f"setting PCAPSplitter split size to {self.split_size} for {cores} cores")
|
58
75
|
|
59
76
|
def split(self) -> List[io.BytesIO] | List[Future]:
|
60
77
|
"Does the actual reading and splitting"
|
61
78
|
# open one for the dpkt reader and one for us independently
|
62
|
-
self.our_data =
|
63
|
-
self.dpkt_data =
|
79
|
+
self.our_data = PCAPDissector.open_maybe_compressed(self.pcap_file)
|
80
|
+
self.dpkt_data = PCAPDissector.open_maybe_compressed(self.pcap_file)
|
81
|
+
|
82
|
+
self.set_split_size()
|
64
83
|
|
65
84
|
# read the first 24 bytes which is the pcap header
|
66
85
|
self.header = self.our_data.read(24)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: traffic-taffy
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.5
|
4
4
|
Summary: A tool for doing differential analysis of pcap files
|
5
5
|
Home-page: https://github.com/hardaker/traffic-taffy
|
6
6
|
Author: Wes Hardaker
|
@@ -9,6 +9,11 @@ Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
10
10
|
Requires-Python: >=3.7
|
11
11
|
Description-Content-Type: text/markdown
|
12
|
+
Requires-Dist: pandas
|
13
|
+
Requires-Dist: rich
|
14
|
+
Requires-Dist: seaborn
|
15
|
+
Requires-Dist: scapy
|
16
|
+
Requires-Dist: dpkt
|
12
17
|
|
13
18
|
# Traffic Analysis of Fluctuating Flows (TAFFy)
|
14
19
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
pcap_compare/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
pcap_compare/cache_info.py,sha256=22Fs5fNU_GzWwcAua_XfEFswRmlM8TmTbHXe2CYKQDE,1468
|
3
|
+
pcap_compare/compare.py,sha256=xVRNmCsdUp1mEZub0SN2CW7osOZMgJuW__Cj9UA2DRE,9169
|
4
|
+
pcap_compare/dissectmany.py,sha256=dxxChV0Qq9LeoPXiS0cCIEKFtO_RJwHxwOoPOZ3rwU8,681
|
5
|
+
pcap_compare/dissector.py,sha256=iEZzLHJ7HubM3-UbZR461irXkZcpM8u_Aoa7GzWUMEY,17571
|
6
|
+
pcap_compare/dissectorresults.py,sha256=LKoyX04Qjc6B7RnqtJgIWsyVnselJ9CygLkMAp3lhw0,647
|
7
|
+
pcap_compare/graph.py,sha256=cioVq9JYNRVqzlRJSKLveGnGSEL9FkXDUyp-Y7osSkM,5935
|
8
|
+
traffic_taffy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
traffic_taffy/cache_info.py,sha256=zpuuJcPzJzyma37YJE4FwKSaOBAmzImDj0Khdn2x1FI,1623
|
10
|
+
traffic_taffy/compare.py,sha256=-PvazIzp1ABvtZpjb1tjG9KAcnZYO_PU29FJpDNwPus,11654
|
11
|
+
traffic_taffy/dissectmany.py,sha256=QCqnIhtMMIqHXPVg4KOIcUYaIyaDR_DCIKLoD41E1C0,2528
|
12
|
+
traffic_taffy/dissector.py,sha256=M75VGvrv2M1NOJ2JkDaxemlxZWr10WswXN2sOTbcX9I,22085
|
13
|
+
traffic_taffy/dissectorresults.py,sha256=LKoyX04Qjc6B7RnqtJgIWsyVnselJ9CygLkMAp3lhw0,647
|
14
|
+
traffic_taffy/explore.py,sha256=QaRqr4UdpO72yxWMfg-WK9YkhQTXZwLzWX6K2qg18d0,6808
|
15
|
+
traffic_taffy/graph.py,sha256=GZsuppEbMeJA9mr3OH-n5ba5sNT7l35P3Qv-eh1MR3E,6221
|
16
|
+
traffic_taffy/pcap_splitter.py,sha256=qDZ0Nd81fIteQRVElyKsD1ncBi4lg07cmx8KPl_q3rY,4585
|
17
|
+
traffic_taffy-0.3.5.dist-info/METADATA,sha256=sA9UeWzu1nJyFxcNOXFJ6dJNFDeGA48URLd4TpwrTx0,985
|
18
|
+
traffic_taffy-0.3.5.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
19
|
+
traffic_taffy-0.3.5.dist-info/entry_points.txt,sha256=xabsmMHgr0Pek8ccdQoW6iAKqljueozDZW-q1hJ79eU,194
|
20
|
+
traffic_taffy-0.3.5.dist-info/top_level.txt,sha256=wjeQaxXSKqUzrRtfbUlbSn8SoaL4VC73yudFAEtnIuo,14
|
21
|
+
traffic_taffy-0.3.5.dist-info/RECORD,,
|
@@ -1,13 +0,0 @@
|
|
1
|
-
traffic_taffy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
traffic_taffy/cache_info.py,sha256=tmPmMJFhz74FDxLoqnQgki5qsUtoJBzkzk3w0ZAen0c,1369
|
3
|
-
traffic_taffy/compare.py,sha256=SW-wT8y9jnWg9Hx3Fa-XdutHasjF-DjfhCIl3DQgoBM,9074
|
4
|
-
traffic_taffy/dissectmany.py,sha256=ZsWKR9eid_Ft-HMLvl-Zy2jyofMeZTzqI8Dl0gZ5AWU,2530
|
5
|
-
traffic_taffy/dissector.py,sha256=iwSUPQujI37h3EM17tic4zmk0GTZe8Xrp7Ov13t51k8,20064
|
6
|
-
traffic_taffy/dissectorresults.py,sha256=LKoyX04Qjc6B7RnqtJgIWsyVnselJ9CygLkMAp3lhw0,647
|
7
|
-
traffic_taffy/graph.py,sha256=gpf_64XBH9rM7uLQhYA3p0PTButfiUvcmY5SNLnq4IA,6569
|
8
|
-
traffic_taffy/pcap_splitter.py,sha256=HKz7QOnekqxUut58TMPK3-dQmP98NIhL7Ii6ofFnxYY,3868
|
9
|
-
traffic_taffy-0.2.dist-info/METADATA,sha256=tzkA86g8DlpwNMUuoSBl3Y4tzqNBjv0FROdqTawVUBc,877
|
10
|
-
traffic_taffy-0.2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
11
|
-
traffic_taffy-0.2.dist-info/entry_points.txt,sha256=xabsmMHgr0Pek8ccdQoW6iAKqljueozDZW-q1hJ79eU,194
|
12
|
-
traffic_taffy-0.2.dist-info/top_level.txt,sha256=wjeQaxXSKqUzrRtfbUlbSn8SoaL4VC73yudFAEtnIuo,14
|
13
|
-
traffic_taffy-0.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|