traffic-taffy 0.3.6__tar.gz → 0.4.1__tar.gz

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.
Files changed (39) hide show
  1. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/PKG-INFO +1 -7
  2. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/pyproject.toml +1 -0
  3. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/setup.py +6 -5
  4. traffic-taffy-0.4.1/traffic_taffy/compare.py +287 -0
  5. traffic-taffy-0.4.1/traffic_taffy/comparison.py +26 -0
  6. traffic-taffy-0.4.1/traffic_taffy/dissection.py +383 -0
  7. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/traffic_taffy/dissectmany.py +20 -18
  8. traffic-taffy-0.4.1/traffic_taffy/dissector.py +260 -0
  9. traffic-taffy-0.4.1/traffic_taffy/dissector_engine/__init__.py +35 -0
  10. traffic-taffy-0.4.1/traffic_taffy/dissector_engine/dpkt.py +98 -0
  11. traffic-taffy-0.4.1/traffic_taffy/dissector_engine/scapy.py +98 -0
  12. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/traffic_taffy/graph.py +23 -90
  13. traffic-taffy-0.4.1/traffic_taffy/graphdata.py +68 -0
  14. traffic-taffy-0.4.1/traffic_taffy/output/__init__.py +118 -0
  15. traffic-taffy-0.4.1/traffic_taffy/output/console.py +72 -0
  16. traffic-taffy-0.4.1/traffic_taffy/output/fsdb.py +50 -0
  17. traffic-taffy-0.4.1/traffic_taffy/output/memory.py +51 -0
  18. traffic-taffy-0.4.1/traffic_taffy/tools/__init__.py +0 -0
  19. {traffic-taffy-0.3.6/traffic_taffy → traffic-taffy-0.4.1/traffic_taffy/tools}/cache_info.py +25 -18
  20. traffic-taffy-0.4.1/traffic_taffy/tools/compare.py +110 -0
  21. traffic-taffy-0.4.1/traffic_taffy/tools/dissect.py +77 -0
  22. traffic-taffy-0.4.1/traffic_taffy/tools/explore.py +686 -0
  23. traffic-taffy-0.4.1/traffic_taffy/tools/graph.py +85 -0
  24. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/traffic_taffy.egg-info/PKG-INFO +1 -7
  25. traffic-taffy-0.4.1/traffic_taffy.egg-info/SOURCES.txt +31 -0
  26. traffic-taffy-0.4.1/traffic_taffy.egg-info/entry_points.txt +6 -0
  27. traffic-taffy-0.3.6/traffic_taffy/compare.py +0 -383
  28. traffic-taffy-0.3.6/traffic_taffy/dissector.py +0 -608
  29. traffic-taffy-0.3.6/traffic_taffy/explore.py +0 -221
  30. traffic-taffy-0.3.6/traffic_taffy/graphdata.py +0 -53
  31. traffic-taffy-0.3.6/traffic_taffy.egg-info/SOURCES.txt +0 -18
  32. traffic-taffy-0.3.6/traffic_taffy.egg-info/entry_points.txt +0 -5
  33. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/README.md +0 -0
  34. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/setup.cfg +0 -0
  35. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/traffic_taffy/__init__.py +0 -0
  36. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/traffic_taffy/dissectorresults.py +0 -0
  37. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/traffic_taffy.egg-info/dependency_links.txt +0 -0
  38. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/traffic_taffy.egg-info/requires.txt +0 -0
  39. {traffic-taffy-0.3.6 → traffic-taffy-0.4.1}/traffic_taffy.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: traffic-taffy
3
- Version: 0.3.6
3
+ Version: 0.4.1
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,12 +9,6 @@ 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
17
- Requires-Dist: pcap-parallel
18
12
 
19
13
  # Traffic Analysis of Fluctuating Flows (TAFFy)
20
14
 
@@ -1,3 +1,4 @@
1
1
  [tool.ruff]
2
2
  ignore = ["E501"] # long lines
3
3
  fixable = ["ALL"] # gulp
4
+ # select = ["ALL"]
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="traffic-taffy",
8
- version="0.3.6",
8
+ version="0.4.1",
9
9
  author="Wes Hardaker",
10
10
  author_email="opensource@hardakers.net",
11
11
  description="A tool for doing differential analysis of pcap files",
@@ -15,10 +15,11 @@ setuptools.setup(
15
15
  packages=setuptools.find_packages(),
16
16
  entry_points={
17
17
  "console_scripts": [
18
- "taffy-compare = traffic_taffy.compare:main",
19
- "taffy-graph = traffic_taffy.graph:main",
20
- "taffy-dissect = traffic_taffy.dissector:main",
21
- "taffy-cache-info = traffic_taffy.cache_info:main",
18
+ "taffy-compare = traffic_taffy.tools.compare:main",
19
+ "taffy-graph = traffic_taffy.tools.graph:main",
20
+ "taffy-dissect = traffic_taffy.tools.dissect:main",
21
+ "taffy-cache-info = traffic_taffy.tools.cache_info:main",
22
+ "taffy-explorer = traffic_taffy.tools.explorer:main",
22
23
  ]
23
24
  },
24
25
  classifiers=[
@@ -0,0 +1,287 @@
1
+ from logging import debug
2
+ from typing import List
3
+ import datetime as dt
4
+ from datetime import datetime
5
+
6
+ from traffic_taffy.comparison import Comparison
7
+ from traffic_taffy.dissectmany import PCAPDissectMany
8
+ from traffic_taffy.dissector import PCAPDissectorLevel
9
+ from traffic_taffy.dissection import Dissection
10
+
11
+
12
+ class PcapCompare:
13
+ "Takes a set of PCAPs to then perform various comparisons upon"
14
+
15
+ REPORT_VERSION: int = 2
16
+
17
+ def __init__(
18
+ self,
19
+ pcap_files: List[str],
20
+ maximum_count: int = 0, # where 0 == all
21
+ deep: bool = True,
22
+ pkt_filter: str | None = None,
23
+ cache_results: bool = False,
24
+ cache_file_suffix: str = "taffy",
25
+ bin_size: int | None = None,
26
+ dissection_level: PCAPDissectorLevel = PCAPDissectorLevel.COUNT_ONLY,
27
+ between_times: List[int] | None = None,
28
+ ignore_list: List[str] = [],
29
+ ) -> None:
30
+ self.pcap_files = pcap_files
31
+ self.deep = deep
32
+ self.maximum_count = maximum_count
33
+ self.pkt_filter = pkt_filter
34
+ self.cache_results = cache_results
35
+ self.dissection_level = dissection_level
36
+ self.between_times = between_times
37
+ self.bin_size = bin_size
38
+ self.cache_file_suffix = cache_file_suffix
39
+ self.ignore_list = ignore_list
40
+
41
+ @property
42
+ def pcap_files(self):
43
+ return self._pcap_files
44
+
45
+ @pcap_files.setter
46
+ def pcap_files(self, new_pcap_files):
47
+ self._pcap_files = new_pcap_files
48
+
49
+ @property
50
+ def reports(self):
51
+ return self._reports
52
+
53
+ @reports.setter
54
+ def reports(self, newvalue):
55
+ self._reports = newvalue
56
+
57
+ def compare_dissections(self, left_side: dict, right_side: dict) -> dict:
58
+ "compares the results from two reports"
59
+
60
+ report = {}
61
+
62
+ # TODO: missing key in right_side (major items added)
63
+ keys = set(left_side.keys())
64
+ keys = keys.union(right_side.keys())
65
+ for key in keys:
66
+ report[key] = {}
67
+
68
+ if key not in left_side:
69
+ left_side[key] = {}
70
+ left_side_total = sum(left_side[key].values())
71
+
72
+ if key not in right_side:
73
+ right_side[key] = {}
74
+ right_side_total = sum(right_side[key].values())
75
+
76
+ new_left_count = 0
77
+ for subkey in left_side[key].keys():
78
+ delta_percentage = 0.0
79
+ total = 0
80
+ if subkey in right_side[key]:
81
+ left_percentage = left_side[key][subkey] / left_side_total
82
+ right_percentage = right_side[key][subkey] / right_side_total
83
+ delta_percentage = right_percentage - left_percentage
84
+ total = right_side[key][subkey] + left_side[key][subkey]
85
+ left_count = left_side[key][subkey]
86
+ right_count = right_side[key][subkey]
87
+ else:
88
+ delta_percentage = -1.0
89
+ left_percentage = left_side[key][subkey] / left_side_total
90
+ right_percentage = 0.0
91
+ total = -left_side[key][subkey]
92
+ left_count = left_side[key][subkey]
93
+ right_count = 0
94
+ new_left_count += 1
95
+
96
+ delta_absolute = right_count - left_count
97
+ report[key][subkey] = {
98
+ "delta_percentage": delta_percentage,
99
+ "delta_absolute": delta_absolute,
100
+ "total": total,
101
+ "left_count": left_count,
102
+ "right_count": right_count,
103
+ "left_percentage": left_percentage,
104
+ "right_percentage": right_percentage,
105
+ }
106
+
107
+ new_right_count = 0
108
+ for subkey in right_side[key].keys():
109
+ if subkey not in report[key]:
110
+ delta_percentage = 1.0
111
+ total = right_side[key][subkey]
112
+ left_count = 0
113
+ right_count = right_side[key][subkey]
114
+ left_percentage = 0.0
115
+ right_percentage = right_side[key][subkey] / right_side_total
116
+ new_right_count += 1 # this value wasn't in the left
117
+
118
+ report[key][subkey] = {
119
+ "delta_percentage": delta_percentage,
120
+ "delta_absolute": right_count,
121
+ "total": total,
122
+ "left_count": left_count,
123
+ "right_count": right_count,
124
+ "left_percentage": left_percentage,
125
+ "right_percentage": right_percentage,
126
+ }
127
+
128
+ if right_side_total == 0:
129
+ right_percent = 100
130
+ else:
131
+ right_percent = new_right_count / right_side_total
132
+ report[key][Dissection.NEW_RIGHT_SUBKEY] = {
133
+ "delta_absolute": new_right_count - new_left_count,
134
+ "total": new_left_count + new_right_count,
135
+ "left_count": new_left_count,
136
+ "right_count": new_right_count,
137
+ "left_percentage": new_left_count / left_side_total,
138
+ "right_percentage": right_percent,
139
+ "delta_percentage": (right_percent - new_left_count / left_side_total),
140
+ }
141
+
142
+ return Comparison(report)
143
+
144
+ def load_pcaps(self) -> None:
145
+ # load the first as a reference pcap
146
+ pdm = PCAPDissectMany(
147
+ self.pcap_files,
148
+ bin_size=self.bin_size,
149
+ maximum_count=self.maximum_count,
150
+ pcap_filter=self.pkt_filter,
151
+ cache_results=self.cache_results,
152
+ cache_file_suffix=self.cache_file_suffix,
153
+ dissector_level=self.dissection_level,
154
+ ignore_list=self.ignore_list,
155
+ )
156
+ results = pdm.load_all()
157
+ return results
158
+
159
+ def compare(self) -> List[Comparison]:
160
+ "Compares each pcap against the original source"
161
+
162
+ dissections = self.load_pcaps()
163
+ self.compare_all(dissections)
164
+ return self.reports
165
+
166
+ def compare_all(self, dissections) -> List[Comparison]:
167
+ reports = []
168
+ if len(self.pcap_files) > 1:
169
+ # multiple file comparison
170
+ reference = next(dissections)
171
+ for other in dissections:
172
+ # compare the two global summaries
173
+
174
+ report = self.compare_dissections(reference.data[0], other.data[0])
175
+ report.title = f"{reference.pcap_file} vs {other.pcap_file}"
176
+
177
+ reports.append(report)
178
+ else:
179
+ # deal with timestamps within a single file
180
+ reference = list(dissections)[0].data
181
+ timestamps = list(reference.keys())
182
+ debug(
183
+ f"found {len(timestamps)} timestamps from {timestamps[2]} to {timestamps[-1]}"
184
+ )
185
+
186
+ for timestamp in range(
187
+ 2, len(timestamps)
188
+ ): # second real non-zero timestamp to last
189
+ time_left = timestamps[timestamp - 1]
190
+ time_right = timestamps[timestamp]
191
+
192
+ # see if we were asked to only use particular time ranges
193
+ if self.between_times and (
194
+ time_left < self.between_times[0]
195
+ or time_right > self.between_times[1]
196
+ ):
197
+ # debug(f"skipping timestamps {time_left} and {time_right}")
198
+ continue
199
+
200
+ debug(f"comparing timestamps {time_left} and {time_right}")
201
+
202
+ report = self.compare_dissections(
203
+ reference[time_left],
204
+ reference[time_right],
205
+ )
206
+
207
+ title_left = datetime.fromtimestamp(time_left, dt.UTC).strftime(
208
+ "%Y-%m-%d %H:%M:%S"
209
+ )
210
+ title_right = datetime.fromtimestamp(time_right, dt.UTC).strftime(
211
+ "%Y-%m-%d %H:%M:%S"
212
+ )
213
+
214
+ report.title = f"time {title_left} vs time {title_right}"
215
+ reports.append(report)
216
+
217
+ continue
218
+
219
+ # takes way too much memory to do it "right"
220
+ # reports.append(
221
+ # {
222
+ # "report": report,
223
+ # "title": f"time {time_left} vs time {time_right}",
224
+ # }
225
+ # )
226
+
227
+ self.reports = reports
228
+ return reports
229
+
230
+
231
+ def compare_add_parseargs(compare_parser, add_subgroup: bool = True):
232
+ if add_subgroup:
233
+ compare_parser = compare_parser.add_argument_group("Comparison result options")
234
+
235
+ compare_parser.add_argument(
236
+ "-t",
237
+ "--print-threshold",
238
+ default=0.0,
239
+ type=float,
240
+ help="Don't print results with abs(percent) less than this threshold",
241
+ )
242
+
243
+ compare_parser.add_argument(
244
+ "-P", "--only-positive", action="store_true", help="Only show positive entries"
245
+ )
246
+
247
+ compare_parser.add_argument(
248
+ "-N", "--only-negative", action="store_true", help="Only show negative entries"
249
+ )
250
+
251
+ compare_parser.add_argument(
252
+ "-x",
253
+ "--top-records",
254
+ default=None,
255
+ type=int,
256
+ help="Show the top N records from each section.",
257
+ )
258
+
259
+ compare_parser.add_argument(
260
+ "-r",
261
+ "--reverse_sort",
262
+ action="store_true",
263
+ help="Reverse the sort order of reports",
264
+ )
265
+
266
+ compare_parser.add_argument(
267
+ "-T",
268
+ "--between-times",
269
+ nargs=2,
270
+ type=int,
271
+ help="For single files, only display results between these timestamps",
272
+ )
273
+
274
+ return compare_parser
275
+
276
+
277
+ def get_comparison_args(args):
278
+ return {
279
+ "maximum_count": args.packet_count or 0,
280
+ "print_threshold": float(args.print_threshold) / 100.0,
281
+ "minimum_count": args.minimum_count,
282
+ "match_string": args.match_string,
283
+ "only_positive": args.only_positive,
284
+ "only_negative": args.only_negative,
285
+ "top_records": args.top_records,
286
+ "reverse_sort": args.reverse_sort,
287
+ }
@@ -0,0 +1,26 @@
1
+ from typing import Dict
2
+
3
+
4
+ class Comparison:
5
+ def __init__(self, contents: list, title: str = ""):
6
+ self.contents = contents
7
+ self.title: str = title
8
+ self.printing_arguments: Dict[str] = {}
9
+
10
+ # title
11
+ @property
12
+ def title(self) -> str:
13
+ return self._title
14
+
15
+ @title.setter
16
+ def title(self, new_title):
17
+ self._title = new_title
18
+
19
+ # report contents -- actual data
20
+ @property
21
+ def contents(self):
22
+ return self._contents
23
+
24
+ @contents.setter
25
+ def contents(self, new_contents):
26
+ self._contents = new_contents