c10net 0.0.2__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.
c10net-0.0.2/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Avionics Test and Analysis Corporation
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
c10net-0.0.2/PKG-INFO ADDED
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: c10net
3
+ Version: 0.0.2
4
+ Summary: Python utility to make Chapter 10/11 file packets available to network tools or replay as UDP packets.
5
+ Author-Email: Stephen Frees <sfrees@avtest.com>
6
+ License-Expression: BSD-3-Clause
7
+ License-File: LICENSE
8
+ Classifier: Programming Language :: Python :: 3 :: Only
9
+ Classifier: Topic :: Software Development :: Testing :: Traffic Generation
10
+ Classifier: Topic :: System :: Monitoring
11
+ Classifier: Topic :: System :: Networking
12
+ Classifier: Development Status :: 2 - Pre-Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Project-URL: Homepage, https://github.com/atac/c10net
16
+ Project-URL: Issues, https://github.com/atac/c10net/issues
17
+ Project-URL: Repository, https://github.com/atac/c10net.git
18
+ Project-URL: Documentation, https://github.com/atac/c10net/blob/main/README.md
19
+ Requires-Python: >=3.9
20
+ Requires-Dist: bitarray>=3.8.0
21
+ Requires-Dist: bitstring>=4.3.1
22
+ Requires-Dist: bitstruct>=8.11.0
23
+ Requires-Dist: colorama>=0.4.6
24
+ Requires-Dist: editables>=0.5
25
+ Requires-Dist: iniconfig>=2.3.0
26
+ Requires-Dist: packaging>=26.0
27
+ Requires-Dist: pluggy>=1.6.0
28
+ Requires-Dist: pychapter10>=1.1.19
29
+ Requires-Dist: Pygments>=2.19.2
30
+ Requires-Dist: pytimedinput>=2.0.1
31
+ Requires-Dist: scapy>=2.7.0
32
+ Provides-Extra: test
33
+ Requires-Dist: pytest>=9.0.2; extra == "test"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pdm_backend>=2.4.7; extra == "dev"
36
+ Provides-Extra: all
37
+ Requires-Dist: c10net[test]; extra == "all"
38
+ Requires-Dist: c10net[dev]; extra == "all"
39
+ Description-Content-Type: text/markdown
40
+
41
+ # c10net
42
+
43
+ **Python utility to make Chapter 10/11 file packets available to network tools or replay as UDP packets.**
44
+
45
+ ## Features
46
+
47
+ - Convert Chapter 10 data into a PCAP file containing UDP packets for analysis.
48
+ - Replay Chapter 10 data over the network, preserving original timing.
49
+ - Filter by channel ID and channel type during conversion or replay.
50
+ - Specify destination port and IP.
51
+
52
+ ## Installation
53
+
54
+ ### Install utility from PyPI:
55
+
56
+ ```bash
57
+ pip install c10net
58
+ ```
59
+
60
+
61
+ **Note:** `c10net` uses libpcap for pcap packet generation.
62
+ On Windows, install [Npcap](https://nmap.org/npcap/) and ensure it is available to your system.
63
+
64
+ ### Install for developers (Windows):
65
+
66
+ ```
67
+ git clone https://github.com/atac/c10net
68
+ py -m venv .venv
69
+ .\.venv\Scripts\activate
70
+ pip install -e .[all]
71
+ ```
72
+
73
+ The `[all]` group installs the `[test]` and `[dev]` dependency groups.
74
+
75
+ ## Usage
76
+
77
+ The package installs a console script `c10net`. Run `c10net -h` for top-level help.
78
+
79
+ ### `convert_pcap`
80
+
81
+ Convert Chapter 10 file to a PCAP file comprised of UDP packets.
82
+
83
+ ```
84
+ c10net convert_pcap "C:\path\to\myfile.ch10"
85
+ ```
86
+
87
+ With default options, a PCAP file is generated at "C:\path\to\myfile.pcap"
88
+
89
+ Use `ch10net convert_pcap -h` for more options.
90
+
91
+ ### `replay`
92
+
93
+ Generate UDP packets from Chapter 10 file and send over a network interface.
94
+
95
+ ```bash
96
+ c10net replay "C:\path\to\myfile.ch10"
97
+ ```
98
+
99
+ With default options, UDP packets are replayed over available network interface with destination IP 127.0.0.1 and port 5006.
100
+
101
+ Use `ch10net replay -h` for more options.
102
+
103
+ ### Network Options
104
+
105
+ * Use `--port` to set the destination port number of generated UDP packets.
106
+ * Use `--ip` to set the destination IP address of generated UDP packets.
107
+
108
+ #### Examples
109
+
110
+ Convert a Chapter 10 file to PCAP:
111
+
112
+ ```bash
113
+ c10net convert_pcap input.ch10 --out output.pcap
114
+ ```
115
+
116
+ Replay over the network (pulse setup packet every second):
117
+
118
+ ```bash
119
+ c10net replay input.ch10 --pulse --ip 192.168.1.10 --port 49152
120
+ ```
121
+
122
+ ## Testing
123
+
124
+ Run the test suite with `pytest` (after installing the optional `[test]` dependency group):
125
+
126
+ ```bash
127
+ pytest -q
128
+ ```
129
+
130
+ ## License
131
+
132
+ BSD-3-Clause
c10net-0.0.2/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # c10net
2
+
3
+ **Python utility to make Chapter 10/11 file packets available to network tools or replay as UDP packets.**
4
+
5
+ ## Features
6
+
7
+ - Convert Chapter 10 data into a PCAP file containing UDP packets for analysis.
8
+ - Replay Chapter 10 data over the network, preserving original timing.
9
+ - Filter by channel ID and channel type during conversion or replay.
10
+ - Specify destination port and IP.
11
+
12
+ ## Installation
13
+
14
+ ### Install utility from PyPI:
15
+
16
+ ```bash
17
+ pip install c10net
18
+ ```
19
+
20
+
21
+ **Note:** `c10net` uses libpcap for pcap packet generation.
22
+ On Windows, install [Npcap](https://nmap.org/npcap/) and ensure it is available to your system.
23
+
24
+ ### Install for developers (Windows):
25
+
26
+ ```
27
+ git clone https://github.com/atac/c10net
28
+ py -m venv .venv
29
+ .\.venv\Scripts\activate
30
+ pip install -e .[all]
31
+ ```
32
+
33
+ The `[all]` group installs the `[test]` and `[dev]` dependency groups.
34
+
35
+ ## Usage
36
+
37
+ The package installs a console script `c10net`. Run `c10net -h` for top-level help.
38
+
39
+ ### `convert_pcap`
40
+
41
+ Convert Chapter 10 file to a PCAP file comprised of UDP packets.
42
+
43
+ ```
44
+ c10net convert_pcap "C:\path\to\myfile.ch10"
45
+ ```
46
+
47
+ With default options, a PCAP file is generated at "C:\path\to\myfile.pcap"
48
+
49
+ Use `ch10net convert_pcap -h` for more options.
50
+
51
+ ### `replay`
52
+
53
+ Generate UDP packets from Chapter 10 file and send over a network interface.
54
+
55
+ ```bash
56
+ c10net replay "C:\path\to\myfile.ch10"
57
+ ```
58
+
59
+ With default options, UDP packets are replayed over available network interface with destination IP 127.0.0.1 and port 5006.
60
+
61
+ Use `ch10net replay -h` for more options.
62
+
63
+ ### Network Options
64
+
65
+ * Use `--port` to set the destination port number of generated UDP packets.
66
+ * Use `--ip` to set the destination IP address of generated UDP packets.
67
+
68
+ #### Examples
69
+
70
+ Convert a Chapter 10 file to PCAP:
71
+
72
+ ```bash
73
+ c10net convert_pcap input.ch10 --out output.pcap
74
+ ```
75
+
76
+ Replay over the network (pulse setup packet every second):
77
+
78
+ ```bash
79
+ c10net replay input.ch10 --pulse --ip 192.168.1.10 --port 49152
80
+ ```
81
+
82
+ ## Testing
83
+
84
+ Run the test suite with `pytest` (after installing the optional `[test]` dependency group):
85
+
86
+ ```bash
87
+ pytest -q
88
+ ```
89
+
90
+ ## License
91
+
92
+ BSD-3-Clause
@@ -0,0 +1,69 @@
1
+ [project]
2
+ name = "c10net"
3
+ version = "0.0.2"
4
+ authors = [
5
+ { name = "Stephen Frees", email = "sfrees@avtest.com" },
6
+ ]
7
+ description = "Python utility to make Chapter 10/11 file packets available to network tools or replay as UDP packets."
8
+ classifiers = [
9
+ "Programming Language :: Python :: 3 :: Only",
10
+ "Topic :: Software Development :: Testing :: Traffic Generation",
11
+ "Topic :: System :: Monitoring",
12
+ "Topic :: System :: Networking",
13
+ "Development Status :: 2 - Pre-Alpha",
14
+ "Intended Audience :: Developers",
15
+ "Operating System :: OS Independent",
16
+ ]
17
+ readme = "README.md"
18
+ requires-python = ">=3.9"
19
+ license = "BSD-3-Clause"
20
+ license-files = [
21
+ "LICEN[CS]E*",
22
+ ]
23
+ dependencies = [
24
+ "bitarray>=3.8.0",
25
+ "bitstring>=4.3.1",
26
+ "bitstruct>=8.11.0",
27
+ "colorama>=0.4.6",
28
+ "editables>=0.5",
29
+ "iniconfig>=2.3.0",
30
+ "packaging>=26.0",
31
+ "pluggy>=1.6.0",
32
+ "pychapter10>=1.1.19",
33
+ "Pygments>=2.19.2",
34
+ "pytimedinput>=2.0.1",
35
+ "scapy>=2.7.0",
36
+ ]
37
+
38
+ [project.optional-dependencies]
39
+ test = [
40
+ "pytest>=9.0.2",
41
+ ]
42
+ dev = [
43
+ "pdm_backend>=2.4.7",
44
+ ]
45
+ all = [
46
+ "c10net[test]",
47
+ "c10net[dev]",
48
+ ]
49
+
50
+ [project.urls]
51
+ Homepage = "https://github.com/atac/c10net"
52
+ Issues = "https://github.com/atac/c10net/issues"
53
+ Repository = "https://github.com/atac/c10net.git"
54
+ Documentation = "https://github.com/atac/c10net/blob/main/README.md"
55
+
56
+ [project.scripts]
57
+ c10net = "c10net:__main__"
58
+
59
+ [build-system]
60
+ requires = [
61
+ "pdm-backend >= 2.4.0",
62
+ ]
63
+ build-backend = "pdm.backend"
64
+
65
+ [tool.pytest.ini_options]
66
+ addopts = "--import-mode=importlib"
67
+ testpaths = [
68
+ "tests",
69
+ ]
File without changes
@@ -0,0 +1,3 @@
1
+ from .c10net import *
2
+
3
+ cli_entry()
@@ -0,0 +1,159 @@
1
+ '''
2
+ Main entry for the c10net tools.
3
+
4
+ This module manages CLI arguments to coordinate module initialization.
5
+ '''
6
+
7
+ import sys
8
+ from threading import Thread, Event
9
+ from pathlib import Path
10
+
11
+ from pytimedinput import timedKey
12
+
13
+ from . import cli
14
+ from . import chapter10_to_pcap
15
+ from . import chapter10_to_replay
16
+ from .tasks import parse_chapter10 as parse_ch10
17
+ from .tasks import chapter10_to_ethernet as ch10_to_eth
18
+ from .tasks import write_to_pcap as write_to_pcap
19
+
20
+ _source_thread = None # Reference to the source thread to determine finished condition
21
+ _threads = [] # List to keep track of threads for cleanup
22
+ _terminate_events = [] # List to keep track of termination events for killing threads
23
+ _finish_events = [] # List to keep track of finish events for clean shutdown of threads
24
+
25
+ should_terminate = Event()
26
+ should_finish = False
27
+
28
+
29
+ def cli_entry():
30
+ args = cli.get_cli_parser().parse_args(sys.argv[1:])
31
+
32
+ #print(args)
33
+
34
+ if (args.command == cli.command_replay):
35
+ stage_replay(args)
36
+ elif (args.command == cli.command_convert_pcap):
37
+ if not args.outfile:
38
+ args.outfile = _get_default_pcap_out_filepath_from_infile(args.in_pathname)
39
+ stage_capture_pcap(args)
40
+ else:
41
+ print("No command given. Use -h for help.")
42
+ sys.exit(1)
43
+
44
+ run()
45
+
46
+ global should_terminate
47
+ if (should_terminate.is_set()):
48
+ terminate_all_threads()
49
+
50
+ for thread in _threads:
51
+ thread.join() # Wait for all threads to finish
52
+
53
+
54
+ def run():
55
+ global _threads
56
+
57
+ for thread in _threads:
58
+ thread.start()
59
+
60
+ wait_for_keypress_with_confirmation()
61
+
62
+ def wait_for_keypress_with_confirmation(prompt_key="Esc", confirm_prompt="Are you sure? (y/n): "):
63
+ """Wait for a specific user keypress, then prompt for confirmation before returning."""
64
+ global should_terminate
65
+
66
+ print(f"Press {prompt_key} to exit...")
67
+
68
+ while not should_terminate.is_set() and not finished():
69
+ # Wait for the keypress
70
+ key_pressed, key_timeout = timedKey(prompt_key, timeout=1)
71
+
72
+ if (not key_timeout):
73
+ # Prompt for confirmation
74
+ response = input(confirm_prompt).strip().lower()
75
+ if response in ('y', 'yes'):
76
+ should_terminate.set()
77
+
78
+ def finished():
79
+ """If source thread is done, set finish events. Returns True if finished."""
80
+ global _source_thread, should_finish
81
+
82
+ if (not should_finish and _source_thread and not _source_thread.is_alive()):
83
+ should_finish = True
84
+ for event in _finish_events:
85
+ event.set()
86
+
87
+ for thread in _threads:
88
+ if thread.is_alive():
89
+ return False
90
+
91
+ return True
92
+
93
+ def terminate_all_threads():
94
+ """Set all termination events to signal threads to exit."""
95
+ for event in _terminate_events:
96
+ event.set()
97
+
98
+
99
+
100
+ def stage_capture_pcap(cli_args):
101
+ #if (cli_args.parallel): # TODO: test this process again
102
+ if (False):
103
+ _threads.append(Thread(
104
+ target=parse_ch10.parse_file,
105
+ args=(
106
+ cli_args.channel_ids,
107
+ cli_args.channel_types,
108
+ cli_args.in_pathname,
109
+ ch10_to_eth.deposit_chapter10_packets
110
+ )))
111
+ _threads.append(Thread(
112
+ target=ch10_to_eth.build_ethernet_packets,
113
+ args=(cli_args, write_to_pcap.deposit_ethernet_packets)
114
+ ))
115
+ _threads.append(Thread(
116
+ target=write_to_pcap.write_packets_to_pcap,
117
+ args=(cli_args.outfile,)
118
+ ))
119
+
120
+ _terminate_events.append(parse_ch10.terminate)
121
+ _terminate_events.append(ch10_to_eth.terminate)
122
+ _terminate_events.append(write_to_pcap.terminate)
123
+
124
+ _finish_events.append(ch10_to_eth.finish)
125
+ _finish_events.append(write_to_pcap.finish)
126
+
127
+ global _source_thread
128
+ _source_thread = _threads[0]
129
+ else:
130
+ chapter10_to_pcap.run_task(cli_args)
131
+ sys.exit(0)
132
+
133
+
134
+
135
+ def stage_replay(cli_args):
136
+ None
137
+ # TODO: implement
138
+ #source_sink = (parse_ch10.retreive_packets, send_udp.deposit_packets)
139
+ # _threads.append(Thread(target=parse_ch10.parse_file, args=(args)))
140
+ # _threads.append(Thread(target=pipe_packets, args=source_sink))
141
+ # _threads.append(Thread(target=send_udp.replay_packets, args=(args)))
142
+
143
+ # _terminate_events.append(parse_ch10.terminate_event)
144
+ # _terminate_events.append(send_udp.terminate_event)
145
+
146
+ # _source_thread = _threads[0]
147
+ #if (cli_args.parallel):
148
+ if (False):
149
+ None
150
+ else:
151
+ chapter10_to_replay.run_task(cli_args)
152
+ sys.exit(0)
153
+
154
+
155
+ def _get_default_pcap_out_filepath_from_infile(in_pathname):
156
+ inpath = Path(in_pathname)
157
+ result = inpath.with_suffix('.pcap')
158
+ return str(result)
159
+
@@ -0,0 +1,53 @@
1
+
2
+ import os
3
+
4
+ from chapter10 import C10
5
+
6
+ from .tasks import parse_chapter10 as pc10
7
+ from .tasks import write_to_pcap as w2pcap
8
+
9
+ from .functions import ethernet_packet_generator as ethernet_gen
10
+ from .functions import progress_bar as bar
11
+
12
+
13
+ def run_task(cli_args):
14
+ eth_gen = ethernet_gen.EthernetGenerator(cli_args)
15
+ pcap = w2pcap.PcapWriter(cli_args.outfile, append=False)
16
+
17
+ pc10._set_filter_parameters(
18
+ cli_args.channel_ids,
19
+ cli_args.channel_types
20
+ )
21
+
22
+ file_pos = 0.0
23
+ size = os.path.getsize(cli_args.in_pathname)
24
+ bar.set_bounds(0, size)
25
+
26
+ have_time = False
27
+ buf = []
28
+
29
+ for packet in C10(cli_args.in_pathname):
30
+ file_pos += packet.packet_length
31
+ bar.update_progress(file_pos)
32
+
33
+ if (not have_time):
34
+ if (packet.parent and packet.parent.last_time is not None):
35
+ have_time = True
36
+ else:
37
+ buf.append(packet)
38
+ continue
39
+
40
+ if (len(buf) > 0):
41
+ for p in buf:
42
+ p.parent.last_time = packet.parent.last_time
43
+ if (pc10._passes_filter(p.channel_id, p.data_type)):
44
+ eth_packets = eth_gen.generate_from_chapter10(p)
45
+ pcap.write(eth_packets)
46
+ buf = []
47
+
48
+ if (pc10._passes_filter(packet.channel_id, packet.data_type)):
49
+ eth_packets = eth_gen.generate_from_chapter10(packet)
50
+ pcap.write(eth_packets)
51
+
52
+ pcap.flush()
53
+ pcap.close()
@@ -0,0 +1,76 @@
1
+
2
+ import os
3
+
4
+ from chapter10 import C10, packet
5
+
6
+ from .tasks import parse_chapter10 as pc10
7
+ from .tasks import udp_replay as replay
8
+
9
+ from .functions import ethernet_packet_generator as ethernet_gen
10
+ from .functions import progress_bar as bar
11
+ from .functions import packet_pulser as pulser
12
+
13
+
14
+
15
+ def run_task(cli_args):
16
+ eth_gen = ethernet_gen.EthernetGenerator(cli_args)
17
+
18
+ setup_pulser = None
19
+ use_pulse = False
20
+ pulse_interval = 1.0
21
+ if cli_args.pulse or cli_args.pulse_interval:
22
+ use_pulse = True
23
+ if (cli_args.pulse_interval):
24
+ pulse_interval = cli_args.pulse_interval
25
+
26
+ pc10._set_filter_parameters(
27
+ cli_args.channel_ids,
28
+ cli_args.channel_types,
29
+ use_pulse
30
+ )
31
+
32
+ have_time = False
33
+ buf = []
34
+
35
+ file_pos = 0.0
36
+ size = os.path.getsize(cli_args.in_pathname)
37
+ bar.set_bounds(0, size)
38
+
39
+ file = C10(cli_args.in_pathname)
40
+
41
+ for packet in file:
42
+ file_pos += packet.packet_length
43
+ bar.update_progress(file_pos)
44
+
45
+ if use_pulse:
46
+ if not setup_pulser:
47
+ if packet.channel_id == 0 and packet.data_type == 0x01:
48
+ setup_pulser = pulser.PacketPulser(packet)
49
+ setup_pulser.set_interval(pulse_interval)
50
+ else:
51
+ setup_packet = setup_pulser.check_pulse()
52
+ if not setup_packet is None:
53
+ buf.append(setup_packet)
54
+
55
+ if (not have_time):
56
+ if (packet.parent and packet.parent.last_time is not None):
57
+ have_time = True
58
+ else:
59
+ buf.append(packet)
60
+ continue
61
+
62
+ if (len(buf) > 0):
63
+ for p in buf:
64
+ p.parent.last_time = packet.parent.last_time
65
+ if (pc10._passes_filter(p.channel_id, p.data_type)):
66
+ eth_packets = eth_gen.generate_from_chapter10(p)
67
+ for eth in eth_packets:
68
+ replay.replay_packet_with_timestamp(eth)
69
+ buf = []
70
+
71
+ if (pc10._passes_filter(packet.channel_id, packet.data_type)):
72
+ eth_packets = eth_gen.generate_from_chapter10(packet)
73
+ for eth in eth_packets:
74
+ replay.replay_packet_with_timestamp(eth)
75
+
76
+