Skip to content

Commit

Permalink
Merge pull request #12 from flo-at/master
Browse files Browse the repository at this point in the history
ART tracker mock tool
  • Loading branch information
jvxgit authored Jun 20, 2024
2 parents 5954933 + 201de60 commit fb25c0c
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 23 deletions.
20 changes: 20 additions & 0 deletions python/art-tracker-mock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Getting started
Setup virtualenv:
```sh
python3 -m venv .venv
. .venv/bin/activate
```
Install dependencies
```sh
pip install -r requirements.txt
```

## 1. Capture network trace with an actual ART tracker
## 2. Run ART Tracker mock
```sh
python3 main.py trace.pcapng 192.168.0.10 65000 127.0.0.1
```
Where `192.168.0.10` is the original destination host and `65000` the destination port.
The new destination host is `127.0.0.1` in this example. The destination port is unchanged.

It keeps sending the packages from the trace file in an endless loop until interrupted by a KeyboardInterrupt.
67 changes: 67 additions & 0 deletions python/art-tracker-mock/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import sys
import socket
import time
from pathlib import Path
from ipaddress import ip_address
from dataclasses import dataclass

from scapy.all import rdpcap, IP, UDP

@dataclass
class Packet:
payload: bytes
dest_port: int
delay: float


def _get_sequence_id(payload: bytes) -> int:
start = 3
end = payload.find(b"\r\n", 3, 50)
return int(payload[start:end])


def _read_packets(pcap_file: Path, pcap_ip: ip_address, pcap_ports: list[int]) -> list[Packet]:
res: list[Packet] = []
with pcap_file.open("rb") as f_in:
packets = rdpcap(f_in)
last_time: float | None = None
for pkt in (pkt for pkt in packets if IP in pkt and UDP in pkt):
ip_layer = pkt[IP]
udp_layer = pkt[UDP]
payload = bytes(udp_layer.payload)
if udp_layer.dport in pcap_ports and payload[0:3] == b"fr " and ip_address(ip_layer.dst) == pcap_ip:
delay: float
if last_time is not None:
delay = float(pkt.time) - last_time
else:
delay = 0.
res.append(Packet(payload, udp_layer.dport, delay))
last_time = float(pkt.time)
return res


def main(pcap_file: Path, pcap_ip: ip_address, pcap_ports: list[int], dest_ip: ip_address):
packets = _read_packets(pcap_file, pcap_ip, pcap_ports)
num_packets = len(packets)
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
sequence_id = 0
while True:
sequence_id += 1
packet = packets[sequence_id % num_packets]
time.sleep(packet.delay)
# TODO spoof sequence id
udp_socket.sendto(packet.payload, (str(dest_ip), packet.dest_port))
finally:
udp_socket.close()


if __name__ == "__main__":
if len(sys.argv) != 5:
sys.stderr.write(f"Usage: {sys.argv[0]} pcap-file pcap-dest-ip pcap-dest-ports dest-ip\npcap-dest-ports are comma separated\n")
sys.exit(1)
pcap_file = Path(sys.argv[1])
pcap_dest_ip = ip_address(sys.argv[2])
pcap_dest_ports = [int(x) for x in sys.argv[3].split(",")]
dest_ip = ip_address(sys.argv[4])
main(pcap_file, pcap_dest_ip, pcap_dest_ports, dest_ip)
1 change: 1 addition & 0 deletions python/art-tracker-mock/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scapy
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ class CjvxMexCallsProfileTpl : public CjvxMexCallsTpl<T>, public CjvxMexCallsPro
{
jvxErrorType res = JVX_NO_ERROR;

CjvxMexCalls::prepare_connect_icon_enter(_common_set_icon.theData_in);
CjvxMexCalls::prepare_connect_icon_enter(this->_common_set_icon.theData_in);

// Run this code before actually preparing this component to allow pre-run configuration in jvxDebugConfigAec
if (_theExtCallHandler)
if (this->_theExtCallHandler)
{
// We need to run this BEFORE we run any prepare call to obtain the
// modified parameter settings in the Matlab startup
Expand All @@ -57,15 +57,15 @@ class CjvxMexCallsProfileTpl : public CjvxMexCallsTpl<T>, public CjvxMexCallsPro
if (res == JVX_NO_ERROR)
{
// Matlab initialization is complete here
res = CjvxMexCalls::prepare_connect_icon_leave(_common_set_icon.theData_in, &_common_set_ocon.theData_out);
res = CjvxMexCalls::prepare_connect_icon_leave(this->_common_set_icon.theData_in, &this->_common_set_ocon.theData_out);
}

if (res == JVX_NO_ERROR)
{
JVX_SAFE_ALLOCATE_2DFIELD_CPP_Z(dbgFldCopyInputs,
jvxData,
_common_set_icon.theData_in->con_params.number_channels,
_common_set_icon.theData_in->con_params.buffersize);
this->_common_set_icon.theData_in->con_params.number_channels,
this->_common_set_icon.theData_in->con_params.buffersize);

if (config.matlab_profiling_enabled)
{
Expand Down Expand Up @@ -93,17 +93,17 @@ class CjvxMexCallsProfileTpl : public CjvxMexCallsTpl<T>, public CjvxMexCallsPro
res = local_deallocate_profiling();
}

CjvxMexCalls::postprocess_connect_icon_enter(_common_set_icon.theData_in);
CjvxMexCalls::postprocess_connect_icon_enter(this->_common_set_icon.theData_in);

JVX_SAFE_DELETE_2DFIELD(dbgFldCopyInputs, _common_set_icon.theData_in->con_params.number_channels);
JVX_SAFE_DELETE_2DFIELD(dbgFldCopyInputs, this->_common_set_icon.theData_in->con_params.number_channels);

// Ignore all return values - if these functions fail we have a more severe problem!!

res = local_postprocess_connect_icon(JVX_CONNECTION_FEEDBACK_CALL(fdb));

res = T::postprocess_connect_icon(JVX_CONNECTION_FEEDBACK_CALL(fdb));

res = CjvxMexCalls::postprocess_connect_icon_leave(_common_set_icon.theData_in);
res = CjvxMexCalls::postprocess_connect_icon_leave(this->_common_set_icon.theData_in);

return res;
};
Expand All @@ -125,15 +125,15 @@ class CjvxMexCallsProfileTpl : public CjvxMexCallsTpl<T>, public CjvxMexCallsPro

// Check if checkbox in Matlab processing was checked
// The checkbox can be activated/deactivated while processing!!
if (_theExtCallHandler)
if (this->_theExtCallHandler)
{
CjvxMexCalls::is_matlab_processing_engaged(&engaged);
}

if(engaged)
{
jvxData** buffers_in = jvx_process_icon_extract_input_buffers<jvxData>(_common_set_icon.theData_in, idx_stage);
jvxData** buffers_out = jvx_process_icon_extract_output_buffers<jvxData>(_common_set_ocon.theData_out);
jvxData** buffers_in = jvx_process_icon_extract_input_buffers<jvxData>(this->_common_set_icon.theData_in, idx_stage);
jvxData** buffers_out = jvx_process_icon_extract_output_buffers<jvxData>(this->_common_set_ocon.theData_out);

// ===================================================
// This case to run Matlab and C code in parallel
Expand All @@ -148,26 +148,26 @@ class CjvxMexCallsProfileTpl : public CjvxMexCallsTpl<T>, public CjvxMexCallsPro
if (!skipCCode)
{
// Copy input data for later usage
for (i = 0; i < _common_set_icon.theData_in->con_params.number_channels; i++)
for (i = 0; i < this->_common_set_icon.theData_in->con_params.number_channels; i++)
{
jvxData* ptrTo = dbgFldCopyInputs[i];
jvxData* ptrFrom = (jvxData*)buffers_in[i];
memcpy(ptrTo, ptrFrom, sizeof(jvxData) * _common_set_icon.theData_in->con_params.buffersize);
memcpy(ptrTo, ptrFrom, sizeof(jvxData) * this->_common_set_icon.theData_in->con_params.buffersize);
}
}

// This lets Matlab run one frame of processing
// ======================================================================================
res = CjvxMexCalls::process_buffers_icon(_common_set_icon.theData_in, &_common_set_ocon.theData_out);
res = CjvxMexCalls::process_buffers_icon(this->_common_set_icon.theData_in, &this->_common_set_ocon.theData_out);
// ======================================================================================

if (!skipCCode)
{
for (i = 0; i < _common_set_icon.theData_in->con_params.number_channels; i++)
for (i = 0; i < this->_common_set_icon.theData_in->con_params.number_channels; i++)
{
jvxData* ptrFrom = dbgFldCopyInputs[i];
jvxData* ptrTo = (jvxData*)buffers_in[i];
memcpy(ptrTo, ptrFrom, sizeof(jvxData) * _common_set_icon.theData_in->con_params.buffersize);
memcpy(ptrTo, ptrFrom, sizeof(jvxData) * this->_common_set_icon.theData_in->con_params.buffersize);
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions sources/jvxLibraries/jvxLexternalCall/include/CjvxMexCallsTpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class CjvxMexCallsTpl : public CjvxMexCalls, public T
initExternalCall();

// Run mex call base classes function to init matlab call handles
res = CjvxMexCalls::select(_common_set_min.theHostRef, static_cast<CjvxProperties*>(this), "",
res = CjvxMexCalls::select(this->_common_set_min.theHostRef, static_cast<CjvxProperties*>(this), "",
_theExtCallObjectName.c_str());
}
return(res);
Expand Down Expand Up @@ -93,25 +93,25 @@ class CjvxMexCallsTpl : public CjvxMexCalls, public T
jvxErrorType res = JVX_NO_ERROR;
// onPrepareConnectMexCalls();

CjvxMexCalls::prepare_sender_to_receiver(_common_set_icon.theData_in);
CjvxMexCalls::prepare_sender_to_receiver(this->_common_set_icon.theData_in);
res = T::prepare_connect_icon(JVX_CONNECTION_FEEDBACK_CALL(fdb));
if (res == JVX_NO_ERROR)
{
CjvxMexCalls::prepare_complete_receiver_to_sender(_common_set_icon.theData_in, &_common_set_ocon.theData_out);
CjvxMexCalls::prepare_complete_receiver_to_sender(this->_common_set_icon.theData_in, &this->_common_set_ocon.theData_out);
}
else
{
CjvxMexCalls::postprocess_sender_to_receiver(_common_set_icon.theData_in);
CjvxMexCalls::postprocess_sender_to_receiver(this->_common_set_icon.theData_in);
}
return res;
};

jvxErrorType postprocess_connect_icon(JVX_CONNECTION_FEEDBACK_TYPE(fdb)) override
{
jvxErrorType res = JVX_NO_ERROR;
CjvxMexCalls::before_postprocess_receiver_to_sender(_common_set_icon.theData_in);
CjvxMexCalls::before_postprocess_receiver_to_sender(this->_common_set_icon.theData_in);
res = T::postprocess_connect_icon(JVX_CONNECTION_FEEDBACK_CALL(fdb));
CjvxMexCalls::postprocess_sender_to_receiver(_common_set_icon.theData_in);
CjvxMexCalls::postprocess_sender_to_receiver(this->_common_set_icon.theData_in);
return res;
};

Expand All @@ -129,7 +129,7 @@ class CjvxMexCallsTpl : public CjvxMexCalls, public T

// This lets Matlab run one frame of processing
// ======================================================================================
res = CjvxMexCalls::process_buffers_icon(_common_set_icon.theData_in, &_common_set_ocon.theData_out);
res = CjvxMexCalls::process_buffers_icon(this->_common_set_icon.theData_in, &this->_common_set_ocon.theData_out);
// ======================================================================================

return T::fwd_process_buffers_icon(mt_mask, idx_stage);
Expand Down

0 comments on commit fb25c0c

Please sign in to comment.