From e857bbb09fea4c809acde22331874b69febba28f Mon Sep 17 00:00:00 2001 From: Allen Hill Date: Fri, 23 Aug 2024 11:02:40 -0700 Subject: [PATCH] Add Wireshark packet dissector plugin and associated tools --- scripts/packet-analysis/Manifest.toml | 251 ++++++++ scripts/packet-analysis/Project.toml | 10 + scripts/packet-analysis/README.md | 11 + scripts/packet-analysis/avclan_plugin.lua | 583 ++++++++++++++++++ scripts/packet-analysis/dev/PcapTools/LICENSE | 19 + .../dev/PcapTools/Project.toml | 21 + .../packet-analysis/dev/PcapTools/README.md | 5 + .../dev/PcapTools/src/PcapTools.jl | 26 + .../dev/PcapTools/src/buffer_reader.jl | 96 +++ .../dev/PcapTools/src/pcap_header.jl | 46 ++ .../dev/PcapTools/src/record.jl | 25 + .../dev/PcapTools/src/record_header.jl | 14 + .../dev/PcapTools/src/splitcap.jl | 102 +++ .../dev/PcapTools/src/stream_reader.jl | 63 ++ .../dev/PcapTools/src/stream_writer.jl | 30 + .../msgdumps/CQ-TS7471LC_bootup.pcap | Bin 0 -> 706 bytes .../msgdumps/amp-settings.pcapng | Bin 0 -> 8812 bytes scripts/packet-analysis/msgdumps/avclan.pcap | Bin 0 -> 26358 bytes .../cd-insertion-functions-eject.pcapng | Bin 0 -> 9708 bytes .../msgdumps/initial-bus-scan.pcap | Bin 0 -> 110 bytes .../msgdumps/lan-test-bus-scan.pcap | Bin 0 -> 350 bytes .../packet-analysis/msgdumps/startup.pcapng | Bin 0 -> 1432 bytes .../tape-insertion-functions-eject.pcapng | Bin 0 -> 3552 bytes scripts/packet-analysis/src/AVCLANPipe.jl | 73 +++ .../src/convert-sr-annotation-export.jl | 55 ++ scripts/packet-analysis/src/pipepackets.jl | 45 ++ 26 files changed, 1475 insertions(+) create mode 100644 scripts/packet-analysis/Manifest.toml create mode 100644 scripts/packet-analysis/Project.toml create mode 100644 scripts/packet-analysis/README.md create mode 100644 scripts/packet-analysis/avclan_plugin.lua create mode 100644 scripts/packet-analysis/dev/PcapTools/LICENSE create mode 100644 scripts/packet-analysis/dev/PcapTools/Project.toml create mode 100644 scripts/packet-analysis/dev/PcapTools/README.md create mode 100644 scripts/packet-analysis/dev/PcapTools/src/PcapTools.jl create mode 100644 scripts/packet-analysis/dev/PcapTools/src/buffer_reader.jl create mode 100644 scripts/packet-analysis/dev/PcapTools/src/pcap_header.jl create mode 100644 scripts/packet-analysis/dev/PcapTools/src/record.jl create mode 100644 scripts/packet-analysis/dev/PcapTools/src/record_header.jl create mode 100644 scripts/packet-analysis/dev/PcapTools/src/splitcap.jl create mode 100644 scripts/packet-analysis/dev/PcapTools/src/stream_reader.jl create mode 100644 scripts/packet-analysis/dev/PcapTools/src/stream_writer.jl create mode 100644 scripts/packet-analysis/msgdumps/CQ-TS7471LC_bootup.pcap create mode 100644 scripts/packet-analysis/msgdumps/amp-settings.pcapng create mode 100644 scripts/packet-analysis/msgdumps/avclan.pcap create mode 100644 scripts/packet-analysis/msgdumps/cd-insertion-functions-eject.pcapng create mode 100644 scripts/packet-analysis/msgdumps/initial-bus-scan.pcap create mode 100644 scripts/packet-analysis/msgdumps/lan-test-bus-scan.pcap create mode 100644 scripts/packet-analysis/msgdumps/startup.pcapng create mode 100644 scripts/packet-analysis/msgdumps/tape-insertion-functions-eject.pcapng create mode 100644 scripts/packet-analysis/src/AVCLANPipe.jl create mode 100644 scripts/packet-analysis/src/convert-sr-annotation-export.jl create mode 100644 scripts/packet-analysis/src/pipepackets.jl diff --git a/scripts/packet-analysis/Manifest.toml b/scripts/packet-analysis/Manifest.toml new file mode 100644 index 0000000..0f21100 --- /dev/null +++ b/scripts/packet-analysis/Manifest.toml @@ -0,0 +1,251 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.10.0" +manifest_format = "2.0" +project_hash = "2d299f8e3abb5302e3c8ed5120acc4d9928016b1" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.1" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[deps.Compat]] +deps = ["UUIDs"] +git-tree-sha1 = "886826d76ea9e72b35fcd000e535588f7b60f21d" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "4.10.1" + + [deps.Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" + + [deps.Compat.weakdeps] + Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[deps.Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" + +[[deps.ExprTools]] +git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" +uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" +version = "0.1.10" + +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" + +[[deps.InlineStrings]] +deps = ["Parsers"] +git-tree-sha1 = "9cc2baf75c6d09f9da536ddf58eb2f29dedaf461" +uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" +version = "1.4.0" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[deps.JLLWrappers]] +deps = ["Artifacts", "Preferences"] +git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.5.0" + +[[deps.LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.4" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "8.4.0+0" + +[[deps.LibGit2]] +deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[deps.LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.6.4+0" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.0+1" + +[[deps.LibSerialPort]] +deps = ["Libdl", "libserialport_jll"] +git-tree-sha1 = "69403284a6217f6e60e76553032a103893b7d910" +uuid = "a05a14c7-6e3b-5ba9-90a2-45558833e1df" +version = "0.5.2" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[deps.Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.2+1" + +[[deps.Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[deps.Mocking]] +deps = ["Compat", "ExprTools"] +git-tree-sha1 = "4cc0c5a83933648b615c36c2b956d94fda70641e" +uuid = "78c3b35d-d492-501b-9361-3d52fe80e533" +version = "0.7.7" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2023.1.10" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" + +[[deps.Parsers]] +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.8.1" + +[[deps.PcapTools]] +deps = ["Dates", "Mmap", "UnixTimes", "UnsafeArrays"] +path = "dev/PcapTools" +uuid = "222fe7e8-3f39-464a-bf97-d9bbb753f246" +version = "1.1.1" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.10.0" + +[[deps.PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.2.0" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.4.1" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[deps.REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[deps.Random]] +deps = ["SHA"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" + +[[deps.Scratch]] +deps = ["Dates"] +git-tree-sha1 = "3bac05bc7e74a75fd9cba4295cde4045d9fe2386" +uuid = "6c6a2e73-6563-6170-7368-637461726353" +version = "1.2.1" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" + +[[deps.TZJData]] +deps = ["Artifacts"] +git-tree-sha1 = "d39314cdbaf5b90a047db33858626f8d1cc973e1" +uuid = "dc5dba14-91b3-4cab-a142-028a31da12f7" +version = "1.0.0+2023c" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" + +[[deps.TimeZones]] +deps = ["Artifacts", "Dates", "Downloads", "InlineStrings", "LazyArtifacts", "Mocking", "Printf", "Scratch", "TZJData", "Unicode", "p7zip_jll"] +git-tree-sha1 = "89e64d61ef3cd9e80f7fc12b7d13db2d75a23c03" +uuid = "f269a46b-ccf7-5d73-abea-4c690281aa53" +version = "1.13.0" + + [deps.TimeZones.extensions] + TimeZonesRecipesBaseExt = "RecipesBase" + + [deps.TimeZones.weakdeps] + RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[deps.UnixTimes]] +deps = ["Dates", "TimeZones"] +git-tree-sha1 = "406de9a6ee22f2030477ed7a50455aef3038bd4b" +uuid = "ab1a18e7-b408-4913-896c-624bb82ed7f4" +version = "1.3.0" + +[[deps.UnsafeArrays]] +git-tree-sha1 = "e7f1c67ba99ac6df440de191fa4d5cbfcbdddcd1" +uuid = "c4a57d5a-5b31-53a6-b365-19f8c011fbd6" +version = "1.0.5" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.13+1" + +[[deps.libserialport_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "aacc5af8e8ba0b48a6feadac7afd4bca5d4e98d8" +uuid = "220460dc-b50e-5ed0-8176-09b0fd261e90" +version = "0.1.3+0" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.52.0+1" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+2" diff --git a/scripts/packet-analysis/Project.toml b/scripts/packet-analysis/Project.toml new file mode 100644 index 0000000..cc8f42f --- /dev/null +++ b/scripts/packet-analysis/Project.toml @@ -0,0 +1,10 @@ +name = "AVCLANPipe" +uuid = "bfacebfd-1bd5-49e7-81bd-5edd8f4074de" +authors = ["Allen Hill "] +version = "0.1.0" + +[deps] +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +LibSerialPort = "a05a14c7-6e3b-5ba9-90a2-45558833e1df" +PcapTools = "222fe7e8-3f39-464a-bf97-d9bbb753f246" +UnixTimes = "ab1a18e7-b408-4913-896c-624bb82ed7f4" diff --git a/scripts/packet-analysis/README.md b/scripts/packet-analysis/README.md new file mode 100644 index 0000000..a3ea514 --- /dev/null +++ b/scripts/packet-analysis/README.md @@ -0,0 +1,11 @@ +# Packet analysis with Wireshark + +- Install the Lua IEBUS/AVCLAN packet dissector to the Wireshark Lua Plugins folder + - Look in Help > About Wireshark dialog, Folders tab, "Personal Lua Plugins". It was `~/.local/lib/wireshark/plugins` for me, but it didn't exist until I manually created it. + - Copy or link `avclan_plugin.lua` to the Wireshark Lua Pluginds folder + - Linking is more convenient if modifying/developing the dissector + `ln -s $(pwd)/avclan_plugin.lua ~/.local/lib/wireshark/plugins/avclan_plugin.lua` +- Install Julia and needed packages (I recommend using [juliaup](https://github.com/JuliaLang/juliaup) or an official binary from the Julialang website. Binaries from your distribution are typically out of date and/or built incorrectly.) + - From this folder, start Julia with the local project environment using `julia --project=@.` + - Run `] instantiate` to install the necessary Julia packages +- Run `julia src/pipepackets.jl | wireshark -k -i -` to pipe packets logged by the Mockingboard over serial into Wireshark. If the Lua dissector is installed correctly, the packets should be correctly recognized and dissected as IEBUS/AVCLAN packets \ No newline at end of file diff --git a/scripts/packet-analysis/avclan_plugin.lua b/scripts/packet-analysis/avclan_plugin.lua new file mode 100644 index 0000000..1a68dd4 --- /dev/null +++ b/scripts/packet-analysis/avclan_plugin.lua @@ -0,0 +1,583 @@ +--[[ + Copyright (C) 2023 Allen Hill + + Portions of the following are based on code from libsigrokdecode PR that is + copyright (C) 2023 Maciej Grela + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +--]] + +local iebusproto = Proto("iebus", "IEBus protocol") + +local f_broadcast = ProtoField.bool("iebus.broadcast", "Broadcast", base.NONE, { [1] = "false", [2] = "true" }) +local f_controller_addr = ProtoField.uint16("iebus.controller", "Controller address", base.HEX, nil, 0x0FFF) +local f_peripheral_addr = ProtoField.uint16("iebus.peripheral", "Peripheral address", base.HEX, nil, 0x0FFF) +local f_control = ProtoField.uint8("iebus.control", "Control field", base.HEX) +local f_length = ProtoField.uint8("iebus.length", "Data length", base.DEC) +local f_data = ProtoField.bytes("iebus.data", "Frame data", base.SPACE) + +iebusproto.fields = { + f_broadcast, + f_controller_addr, + f_peripheral_addr, + f_control, + f_length, + f_data +} + +local controlbits = { + [0x0] = "READ PERIPH STATUS (SSR)", + [0x1] = "UNDEFINED", + [0x2] = "UNDEFINED", + [0x3] = "READ & LOCK DATA", + [0x4] = "READ LOCK ADDR (lo8)", + [0x5] = "READ LOCK ADDR (hi4)", + [0x6] = "READ & UNLOCK PERIPH STATUS (SSR)", + [0x7] = "READ DATA", + [0x8] = "UNDEFINED", + [0x9] = "UNDEFINED", + [0xA] = "WRITE & LOCK CMD", + [0xB] = "WRITE & LOCK DATA", + [0xC] = "UNDEFINED", + [0xD] = "UNDEFINED", + [0xE] = "WRITE CMD", + [0xF] = "WRITE DATA" +} + +local periphstatusbits = { + [0] = "TX BUFFER EMPTY", + [1] = "RX BUFFER EMPTY", + [2] = "UNIT LOCKED", + [4] = "TX ENABLED", + [6] = "IEBUS MODE 1", + [7] = "IEBUS MODE 2", +} + +-- Expert info for unknown packet types, set as group PI_UNDECODED, severity PI_NOTE or PI_WARN +-- Expert info for LAN Check [0x3, SW_ID, 0x01, 0x20] as severity PI_CHAT + +function iebusproto.dissector(buffer, pinfo, tree) + length = buffer:len() + if length == 0 then return end + + pinfo.cols.protocol = iebusproto.name + + local subtree = tree:add(iebusproto, buffer(), "IEBus frame") + + subtree:add_le(f_broadcast, buffer(0,1)) + pinfo.cols.src = buffer(1,2):bytes():tohex():sub(2,-1) + pinfo.cols.dst = buffer(3,2):bytes():tohex():sub(2,-1) + + local control_item, control = subtree:add_packet_field(f_control, buffer(5,1), ENC_LITTLE_ENDIAN) + -- print("control val" .. control) + control_item:append_text(" (" .. controlbits[buffer(5,1):uint()] .. ")") + + local _, datalen = subtree:add_packet_field(f_length, buffer(6,1), ENC_LITTLE_ENDIAN) + + local data = subtree:add(iebusproto, buffer(), "IEBus message") + data:add(f_data, buffer(7, datalen)) + +end + +local udlt = DissectorTable.get("wtap_encap") +udlt:add(wtap.USER15, iebusproto) + +local avclanproto = Proto("avclan", "AVCLAN protocol") + +local known_devices_names = { + ["COMM_CTRL"] = 0x01, + ["COMMUNICATION v1"] = 0x11, + ["COMMUNICATION v2"] = 0x12, + ["SW"] = 0x21, + ["SW_NAME"] = 0x23, + ["SW_CONVERTING"] = 0x24, + ["CMD_SW"] = 0x25, + ["STATUS"] = 0x31, + ["BEEP_HU"] = 0x28, + ["BEEP_SPEAKERS"] = 0x29, + ["FRONT_PSNG_MONITOR"] = 0x34, + ["CD_CHANGER2"] = 0x43, + ["BLUETOOTH_TEL"] = 0x55, + ["INFO_DRAWING"] = 0x56, + ["NAV_ECU"] = 0x58, + ["CAMERA"] = 0x5C, + ["CLIMATE_DRAWING"] = 0x5D, + ["AUDIO_DRAWING"] = 0x5E, + ["TRIP_INFO_DRAWING"] = 0x5F, + ["TUNER"] = 0x60, + ["TAPE_DECK"] = 0x61, + ["CD"] = 0x62, + ["CD_CHANGER"] = 0x63, + ["AUDIO_AMP"] = 0x74, + ["GPS"] = 0x80, + ["VOICE_CTRL"] = 0x85, + ["CLIMATE_CTRL_DEV"] = 0xE0, + ["TRIP_INFO"] = 0xE5, +} + +local known_devices = { + [0x01] = "COMM_CTRL", + [0x11] = "COMMUNICATION v1", + [0x12] = "COMMUNICATION v2", + [0x21] = "SW", + [0x23] = "SW_NAME", + [0x24] = "SW_CONVERTING", + [0x25] = "CMD_SW", + [0x31] = "STATUS", + [0x28] = "BEEP_HU", + [0x29] = "BEEP_SPEAKERS", + [0x34] = "FRONT_PSNG_MONITOR", + [0x43] = "CD_CHANGER2", + [0x55] = "BLUETOOTH_TEL", + [0x56] = "INFO_DRAWING", + [0x58] = "NAV_ECU", + [0x5C] = "CAMERA", + [0x5D] = "CLIMATE_DRAWING", + [0x5E] = "AUDIO_DRAWING", + [0x5F] = "TRIP_INFO_DRAWING", + [0x60] = "TUNER", + [0x61] = "TAPE_DECK", + [0x62] = "CD", + [0x63] = "CD_CHANGER", + [0x74] = "AUDIO_AMP", + [0x80] = "GPS", + [0x85] = "VOICE_CTRL", + [0xE0] = "CLIMATE_CTRL_DEV", + [0xE5] = "TRIP_INFO", +} + +local f_from_device = ProtoField.uint8("avclan.from_device", "From device", base.HEX, known_devices) +local f_to_device = ProtoField.uint8("avclan.to_device", "To device", base.HEX, known_devices) +local f_active_device = ProtoField.uint8("avclan.active_device", "Active device", base.HEX, known_devices) + +local known_actions_names = { + -- LAN related + ["LIST_FUNCTIONS_REQ"] = 0x00, + ["LIST_FUNCTIONS_RESP"] = 0x10, + ["RESTART_LAN"] = 0x01, + ["LANCHECK_END_REQ"] = 0x08, + ["LANCHECK_END_RESP"] = 0x18, + ["LANCHECK_SCAN_REQ"] = 0x0a, + ["LANCHECK_SCAN_RESP"] = 0x1a, + ["LANCHECK_REQ"] = 0x0c, + ["LANCHECK_RESP"] = 0x1c, + ["PING_REQ"] = 0x20, + ["PING_RESP"] = 0x30, + + -- Device switching + ["DISABLE_FUNCTION_REQ"] = 0x43, + ["DISABLE_FUNCTION_RESP"] = 0x53, + ["ENABLE_FUNCTION_REQ"] = 0x42, + ["ENABLE_FUNCTION_RESP"] = 0x52, + + ["ADVERTISE_FUNCTION"] = 0x45, + ["GENERAL_QUERY"] = 0x46, + + -- Physical interface + ["EJECT"] = 0x80, + ["DISC_UP"] = 0x90, + ["DISC_DOWN"] = 0x91, + ["PWRVOL_KNOB_RIGHTHAND_TURN"] = 0x9c, + ["PWRVOL_KNOB_LEFTHAND_TURN"] = 0x9d, + ["TRACK_SEEK_UP"] = 0x94, + ["TRACK_SEEK_DOWN"] = 0x95, + ["CD_ENABLE_SCAN"] = 0xa6, + ["CD_DISABLE_SCAN"] = 0xa7, + ["CD_ENABLE_REPEAT"] = 0xa0, + ["CD_DISABLE_REPEAT"] = 0xa1, + ["CD_ENABLE_RANDOM"] = 0xb0, + ["CD_DISABLE_RANDOM"] = 0xb1, + + -- CD functions + -- Events + ["INSERTED_CD"] = 0x50, + ["REMOVED_CD"] = 0x51, + + -- Requests + ["REQUEST_REPORT"] = 0xe0, + ["REQUEST_REPORT2"] = 0xe2, + ["REQUEST_LOADER2"] = 0xe4, + ["REQUEST_TRACK_NAME"] = 0xed, + + -- Reports + ["REPORT"] = 0xf1, + ["REPORT2"] = 0xf2, + ["REPORT_LOADER"] = 0xf3, + ["REPORT_LOADER2"] = 0xf4, + ["REPORT_TOC"] = 0xf9, + ["REPORT_TRACK_NAME"] = 0xfd, +} +local known_actions = { + -- LAN related + [0x00] = "LIST_FUNCTIONS_REQ", + [0x10] = "LIST_FUNCTIONS_RESP", + [0x01] = "RESTART_LAN", + [0x08] = "LANCHECK_END_REQ", + [0x18] = "LANCHECK_END_RESP", + [0x0a] = "LANCHECK_SCAN_REQ", + [0x1a] = "LANCHECK_SCAN_RESP", + [0x0c] = "LANCHECK_REQ", + [0x1c] = "LANCHECK_RESP", + [0x20] = "PING_REQ", + [0x30] = "PING_RESP", + + -- Used when HU is switching between Radio and CD + [0x43] = "DISABLE_FUNCTION_REQ", + [0x53] = "DISABLE_FUNCTION_RESP", + [0x42] = "ENABLE_FUNCTION_REQ", + [0x52] = "ENABLE_FUNCTION_RESP", + + [0x45] = "ADVERTISE_FUNCTION", + [0x46] = "GENERAL_QUERY", + + -- Physical interface + [0x80] = "EJECT", + [0x90] = "DISC_UP", + [0x91] = "DISC_DOWN", + [0x9c] = "PWRVOL_KNOB_RIGHTHAND_TURN", + [0x9d] = "PWRVOL_KNOB_LEFTHAND_TURN", + [0x94] = "TRACK_SEEK_UP", + [0x95] = "TRACK_SEEK_DOWN", + [0xa6] = "CD_ENABLE_SCAN", + [0xa7] = "CD_DISABLE_SCAN", + [0xa0] = "CD_ENABLE_REPEAT", + [0xa1] = "CD_DISABLE_REPEAT", + [0xb0] = "CD_ENABLE_RANDOM", + [0xb1] = "CD_DISABLE_RANDOM", + + -- CD functions + -- Events + [0x50] = "INSERTED_CD", + [0x51] = "REMOVED_CD", + + -- Requests + [0xe0] = "REQUEST_REPORT", + [0xe2] = "REQUEST_REPORT2", + [0xe4] = "REQUEST_LOADER2", + [0xed] = "REQUEST_TRACK_NAME", + + -- Reports + [0xf1] = "REPORT", + [0xf2] = "REPORT2", + [0xf3] = "REPORT_LOADER", + [0xf4] = "REPORT_LOADER2", + [0xf9] = "REPORT_TOC", + [0xfd] = "REPORT_TRACK_NAME", +} + +local f_action = ProtoField.uint8("avclan.action", "Action", base.HEX, known_actions) +local f_functions = ProtoField.bytes("avclan.functions", "Functions", base.SPACE, "Device functions") + +local f_ping_count = ProtoField.uint8("avclan.ping.count", "Ping count") + +local f_radio_active = ProtoField.bool("avclan.radio.active", "Radio", base.NONE, {"ON", "OFF"}) +local f_radio_status = ProtoField.uint8("avclan.radio.status", "Radio status", base.HEX, + { + [0x00] = "OFF", + [0x01] = "READY", + [0x06] = "SCAN UP", + [0x07] = "SCAN DOWN", + [0x0A] = "AST SEARCH", + [0x27] = "MANUAL" + } +) +local f_radio_flags = ProtoField.uint8("avclan.radio.flags", "Radio flags") +local f_radio_flags2 = ProtoField.uint8("avclan.radio.flags2", "Radio flags (byte 2)") + +local f_radioflag_st = ProtoField.bool("avclan.radio.flags.st", "Stereo", 8, {"Set", "Not set"}, 0x04) +local f_radioflag_ta = ProtoField.bool("avclan.radio.flags.ta", "TA", 8, {"Set", "Not set"}, 0x08) +local f_radioflag_reg = ProtoField.bool("avclan.radio.flags.reg", "REG", 8, {"Set", "Not set"}, 0x10) +local f_radioflag_af = ProtoField.bool("avclan.radio.flags.af", "AF", 8, {"Set", "Not set"}, 0x40) + +local f_radio_band = ProtoField.uint8("avclan.radio.band", "Radio band", base.HEX, + {[0x8] = "FM", [0xC] = "AM (Long-wave)", [0x0] = "AM (Medium-wave)"}, 0xF0) +local f_radio_bandnumber = ProtoField.int8("avclan.radio.bandnumber", "Radio band number", base.DEC, nil, 0x0F) +local f_radio_freq = ProtoField.uint16("avclan.radio.freq", "Radio frequency") + +local f_amp_volume = ProtoField.uint8("avclan.amp.volume", "Volume", base.DEC) +local f_amp_bass = ProtoField.uint8("avclan.amp.bass", "Bass", base.HEX, { + [0x0B] = "-5", [0x0C] = "-4", [0x0D] = "-3", [0x0E] = "-2", [0x0F] = "-1", + [0x10] = "0", + [0x11] = "+1", [0x12] = "+2", [0x13] = "+3", [0x14] = "+4", [0x15] = "+5" +}) +local f_amp_mid = ProtoField.uint8("avclan.amp.mid", "Mid", base.HEX, { + [0x0B] = "-5", [0x0C] = "-4", [0x0D] = "-3", [0x0E] = "-2", [0x0F] = "-1", + [0x10] = "0", + [0x11] = "+1", [0x12] = "+2", [0x13] = "+3", [0x14] = "+4", [0x15] = "+5" +}) +local f_amp_treble = ProtoField.uint8("avclan.amp.treble", "Treble", base.HEX, { + [0x0B] = "-5", [0x0C] = "-4", [0x0D] = "-3", [0x0E] = "-2", [0x0F] = "-1", + [0x10] = "0", + [0x11] = "+1", [0x12] = "+2", [0x13] = "+3", [0x14] = "+4", [0x15] = "+5" +}) +local f_amp_fade = ProtoField.uint8("avclan.amp.fade", "Fade (forward/rear)", base.HEX, { + [0x09] = "-7", [0x0A] = "-6", [0x0B] = "-5", [0x0C] = "-4", [0x0D] = "-3", [0x0E] = "-2", [0x0F] = "-1", + [0x10] = "0", + [0x11] = "+1", [0x12] = "+2", [0x13] = "+3", [0x14] = "+4", [0x15] = "+5", [0x16] = "+6", [0x17] = "+7" +}) +local f_amp_balance = ProtoField.uint8("avclan.amp.balance", "Balance (right/left)", base.HEX, { + [0x09] = "-7", [0x0A] = "-6", [0x0B] = "-5", [0x0C] = "-4", [0x0D] = "-3", [0x0E] = "-2", [0x0F] = "-1", + [0x10] = "0", + [0x11] = "+1", [0x12] = "+2", [0x13] = "+3", [0x14] = "+4", [0x15] = "+5", [0x16] = "+6", [0x17] = "+7" +}) + +local f_cd_slots = ProtoField.uint8("avclan.cd.slots", "CD player disc slots") +local f_cd_slot1 = ProtoField.bool("avclan.cd.slot1", "Slot 1", 6, {"Filled", "Empty"}, 0x01) +local f_cd_slot2 = ProtoField.bool("avclan.cd.slot1", "Slot 2", 6, {"Filled", "Empty"}, 0x02) +local f_cd_slot3 = ProtoField.bool("avclan.cd.slot1", "Slot 3", 6, {"Filled", "Empty"}, 0x04) +local f_cd_slot4 = ProtoField.bool("avclan.cd.slot1", "Slot 4", 6, {"Filled", "Empty"}, 0x08) +local f_cd_slot5 = ProtoField.bool("avclan.cd.slot1", "Slot 5", 6, {"Filled", "Empty"}, 0x10) +local f_cd_slot6 = ProtoField.bool("avclan.cd.slot1", "Slot 6", 6, {"Filled", "Empty"}, 0x20) + +local f_cd_state = ProtoField.uint8("avclan.cd.state", "CD player state") +local f_cd_open = ProtoField.bool("avclan.cd.state.open", "OPEN", 8, nil, 0x01) +local f_cd_err1 = ProtoField.bool("avclan.cd.state.err1", "ERR1", 8, nil, 0x02) +local f_cd_seeking = ProtoField.bool("avclan.cd.state.seeking", "SEEKING", 8, nil, 0x08) +local f_cd_playback = ProtoField.bool("avclan.cd.state.playback", "PLAYBACK", 8, nil, 0x10) +local f_cd_seeking_track = ProtoField.bool("avclan.cd.state.seeking_track", "SEEKING_TRACK", 8, nil, 0x20) +local f_cd_loading = ProtoField.bool("avclan.cd.state.loading", "LOADING", 8, nil, 0x80) + +local f_cd_disc = ProtoField.uint8("avclan.cd.disc", "Current disc", base.DEC) +local f_cd_track = ProtoField.uint8("avclan.cd.track", "Track number", base.HEX) +local f_cd_min = ProtoField.uint8("avclan.cd.mins", "CD track play time, minutes", base.HEX) +local f_cd_sec = ProtoField.uint8("avclan.cd.secs", "CD track play time, seconds", base.HEX) + +local f_cd_flags = ProtoField.uint8("avclan.cd.flags", "CD player flags", base.HEX, { + [0x02] = "DISK_RANDOM", + [0x04] = "RANDOM", + [0x08] = "DISK_REPEAT", + [0x10] = "REPEAT", + [0x20] = "DISK_SCAN", + [0x40] = "SCAN", +}) + +avclanproto.fields = { + f_from_device, + f_to_device, + f_active_device, + f_action, + f_functions, + f_ping_count, + f_radio_active, + f_radio_status, + f_radio_flags, + f_radio_flags2, + f_radio_band, + f_radio_bandnumber, + f_radio_freq, + f_radioflag_af, + f_radioflag_reg, + f_radioflag_st, + f_radioflag_ta, + f_amp_volume, + f_amp_bass, + f_amp_mid, + f_amp_treble, + f_amp_fade, + f_amp_balance, + f_cd_slots, + f_cd_slot1, + f_cd_slot2, + f_cd_slot3, + f_cd_slot4, + f_cd_slot5, + f_cd_slot6, + f_cd_state, + f_cd_open, + f_cd_err1, + f_cd_seeking, + f_cd_playback, + f_cd_seeking_track, + f_cd_loading, + f_cd_disc, + f_cd_track, + f_cd_min, + f_cd_sec, + f_cd_flags, +} + +local pe_unhandled_msg = ProtoExpert.new("avclan.expert", "Message not decoded", + expert.group.UNDECODED, expert.severity.WARN) +-- local pe_lan_check = ProtoExpert.new("avclan.lan.expert", "") +local pe_ping_req = ProtoExpert.new("avclan.ping_req.expert", "Ping request", + expert.group.SEQUENCE, expert.severity.CHAT) +local pe_ping_resp = ProtoExpert.new("avclan.ping_resp.expert", "Ping response", + expert.group.SEQUENCE, expert.severity.CHAT) + +avclanproto.experts = { + pe_unhandled_msg, + pe_ping_req, + pe_ping_resp, +} + +local field_from_device = Field.new("avclan.from_device") +local field_to_device = Field.new("avclan.to_device") +local field_action = Field.new("avclan.action") +local field_radio_freq = Field.new("avclan.radio.freq") +local field_radio_band = Field.new("avclan.radio.band") +local field_cd_disc = Field.new("avclan.cd.disc") +local field_cd_track = Field.new("avclan.cd.track") +local field_cd_min = Field.new("avclan.cd.mins") +local field_cd_sec = Field.new("avclan.cd.secs") + +function avclanproto.dissector(buffer, pinfo, tree) + local length = buffer:len() + if length == 0 then + return + end + + iebusproto.dissector(buffer, pinfo, tree) + + local subtree = tree:add(avclanproto, buffer(7,-1), "AVCLAN message") + local offset = 7 + if buffer(7,1):uint() == 0 then + offset = 8 + subtree:add(f_from_device, buffer(offset+0,1)) + subtree:add(f_to_device, buffer(offset+1,1)) + else + subtree:add(f_from_device, buffer(offset+0,1)) + subtree:add(f_to_device, buffer(offset+1,1)) + end + + local from_device = field_from_device().value + local to_device = field_to_device().value + + if from_device == known_devices_names["CMD_SW"] then + subtree:add(f_action, buffer(offset+2,1)) + elseif from_device == known_devices_names["COMMUNICATION v1"] or + from_device == known_devices_names["COMMUNICATION v2"] then + if to_device == known_devices_names["COMM_CTRL"] then + subtree:add(f_action, buffer(offset+2,1)) + local action = field_action().value + if action == known_actions_names["ADVERTISE_FUNCTION"] then + subtree:add(f_active_device, buffer(offset+3,1)) + elseif action == known_actions_names["PING_REQ"] then + subtree:add(f_ping_count, buffer(offset+3,1)) + subtree:add_proto_expert_info(pe_ping_req, "Ping request " .. buffer(offset+3,1):uint()) + elseif known_actions[action] then + else + subtree:add_proto_expert_info(pe_unhandled_msg) + end + else + subtree:add_proto_expert_info(pe_unhandled_msg) + end + elseif from_device == known_devices_names["COMM_CTRL"] then + if to_device == known_devices_names["COMMUNICATION v1"] or + to_device == known_devices_names["COMMUNICATION v2"] then + local action_tree = subtree:add(f_action, buffer(offset+2,1)) + local action = field_action().value + if action == known_actions_names["PING_RESP"] then + subtree:add(f_ping_count, buffer(offset+3,1)) + subtree:add_proto_expert_info(pe_ping_resp, "Ping response " .. buffer(offset+3,1):uint()) + elseif action == known_actions_names["LIST_FUNCTIONS_RESP"] then + local functions = action_tree:add(f_functions, buffer(offset+3)) + functions:append_text(" (") + for v = 0,(buffer:bytes(offset+3)):len()-1 do + if known_devices[buffer(offset+3+v,1):uint()] then + functions:append_text(" " .. known_devices[buffer(offset+3+v,1):uint()]) + else + functions:append_text(" UNKNOWN_DEVICE") + end + end + functions:append_text(" ) ") + elseif known_actions[action] then + else + subtree:add_proto_expert_info(pe_unhandled_msg) + end + elseif to_device == known_actions_names["LANCHECK_SCAN_REQ"] or + to_device == known_actions_names["LANCHECK_REQ"] or + to_device == known_actions_names["LANCHECK_END_REQ"] then + subtree:add(f_action, buffer(offset+1,1)) + elseif to_device == 0x00 then + subtree:add(f_action, buffer(offset+2,1)) + else + subtree:add_proto_expert_info(pe_unhandled_msg) + end + elseif from_device == known_devices_names["STATUS"] then + subtree:add(f_action, buffer(offset+2,1)) + elseif from_device == known_devices_names["TUNER"] then + subtree:add(f_action, buffer(offset+2,1)) + local radiotree = subtree:add(avclanproto, buffer(offset,10), "Device: Radio") + radiotree:add_le(f_radio_active, buffer(offset+3,1)) + radiotree:add_le(f_radio_status, buffer(offset+4,1)) + radiotree:add_le(f_radio_band, buffer(offset+5,1)) + radiotree:add(f_radio_bandnumber, buffer(offset+5,1)) + local freqtree = radiotree:add(f_radio_freq, buffer(offset+6,2)) + local radio_band = buffer(offset+5,1):uint() + local freq = field_radio_freq().value + if bit32.band(radio_band, 0xF0) == 0x80 then + freqtree:append_text(" (" .. 87.5+(freq-1)*.05 .. " MHz)") + elseif bit32.band(radio_band, 0xF0) == 0xC0 then + freqtree:append_text(" (" .. 153+(freq-1)*1 .. " kHz)") + elseif bit32.band(radio_band, 0xF0) == 0x00 then + freqtree:append_text(" (" .. 522+(freq-1)*9 .. " kHz)") + end + + local flags = radiotree:add(f_radio_flags, buffer(15,1)) + flags:add(f_radioflag_st, buffer(15,1)) + flags:add(f_radioflag_ta, buffer(15,1)) + flags:add(f_radioflag_reg, buffer(15,1)) + flags:add(f_radioflag_af, buffer(15,1)) + radiotree:add(f_radio_flags2, buffer(16,1)) + elseif from_device == known_devices_names["AUDIO_AMP"] then + subtree:add(f_action, buffer(offset+2,1)) + local amptree = subtree:add(avclanproto, buffer(offset,10), "Device: Audio amplifier") + + amptree:add(f_amp_volume, buffer(offset+4,1)) + amptree:add(f_amp_balance, buffer(offset+5,1)) + amptree:add(f_amp_fade, buffer(offset+6,1)) + amptree:add(f_amp_bass, buffer(offset+7,1)) + amptree:add(f_amp_mid, buffer(offset+8,1)) + amptree:add(f_amp_treble, buffer(offset+9,1)) + elseif from_device == known_devices_names["CD"] or + from_device == known_devices_names["CD_CHANGER"] or + from_device == known_devices_names["CD_CHANGER2"] then + subtree:add(f_action, buffer(offset+2,1)) + + local action = field_action().value + if action == known_actions_names["REPORT"] then + local cdtree = subtree:add(avclanproto, buffer(offset,9), "Device: CD player") + local cd_slots = cdtree:add(f_cd_slots, buffer(offset+3,1)) + cd_slots:add(f_cd_slot1, buffer(offset+3,1)) + cd_slots:add(f_cd_slot2, buffer(offset+3,1)) + cd_slots:add(f_cd_slot3, buffer(offset+3,1)) + cd_slots:add(f_cd_slot4, buffer(offset+3,1)) + cd_slots:add(f_cd_slot5, buffer(offset+3,1)) + cd_slots:add(f_cd_slot6, buffer(offset+3,1)) + + local cd_state = cdtree:add(f_cd_state, buffer(offset+4,1)) + cd_state:add(f_cd_open, buffer(offset+4,1)) + cd_state:add(f_cd_err1, buffer(offset+4,1)) + cd_state:add(f_cd_seeking, buffer(offset+4,1)) + cd_state:add(f_cd_playback, buffer(offset+4,1)) + cd_state:add(f_cd_seeking_track, buffer(offset+4,1)) + cd_state:add(f_cd_loading, buffer(offset+4,1)) + local cd_status = cdtree:add(avclanproto, buffer(offset+5,-1), "") + cd_status:add(f_cd_disc, buffer(offset+5,1)) + cd_status:add(f_cd_track, buffer(offset+6,1)) + cd_status:add(f_cd_min, buffer(offset+7,1)) + cd_status:add(f_cd_sec, buffer(offset+8,1)) + cd_status:append_text("Disc " .. field_cd_disc().value .. ", ") + cd_status:append_text("track " .. tostring(buffer(offset+6,1)):gsub("(.)(.)", "%1%2") .. ", ") + cd_status:append_text("time " .. tostring(buffer(offset+7,1)):gsub("0x(.)(.)", "%1%2") .. ":") + cd_status:append_text(tostring(buffer(offset+8,1)):gsub("(.)(.)", "%1%2")) + cdtree:add(f_cd_flags, buffer(offset+9,1)) + end + else + subtree:add_proto_expert_info(pe_unhandled_msg) + end +end + +-- for i,v in ipairs(DissectorTable.list()) do print(v) end +udlt:add(wtap.USER15, avclanproto) diff --git a/scripts/packet-analysis/dev/PcapTools/LICENSE b/scripts/packet-analysis/dev/PcapTools/LICENSE new file mode 100644 index 0000000..701f519 --- /dev/null +++ b/scripts/packet-analysis/dev/PcapTools/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Christian Rorvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/scripts/packet-analysis/dev/PcapTools/Project.toml b/scripts/packet-analysis/dev/PcapTools/Project.toml new file mode 100644 index 0000000..f819b5d --- /dev/null +++ b/scripts/packet-analysis/dev/PcapTools/Project.toml @@ -0,0 +1,21 @@ +name = "PcapTools" +uuid = "222fe7e8-3f39-464a-bf97-d9bbb753f246" +authors = ["Christian Rorvik "] +version = "1.1.1" + +[deps] +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Mmap = "a63ad114-7e13-5084-954f-fe012c677804" +UnixTimes = "ab1a18e7-b408-4913-896c-624bb82ed7f4" +UnsafeArrays = "c4a57d5a-5b31-53a6-b365-19f8c011fbd6" + +[compat] +UnixTimes = "1" +UnsafeArrays = "1" +julia = "1.4" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/scripts/packet-analysis/dev/PcapTools/README.md b/scripts/packet-analysis/dev/PcapTools/README.md new file mode 100644 index 0000000..8a29171 --- /dev/null +++ b/scripts/packet-analysis/dev/PcapTools/README.md @@ -0,0 +1,5 @@ +# PcapTools + +[![Build Status](https://travis-ci.com/ancapdev/PcapTools.jl.svg?branch=master)](https://travis-ci.com/ancapdev/PcapTools.jl) + +[![Coverage Status](https://coveralls.io/repos/github/ancapdev/PcapTools.jl/badge.svg)](https://coveralls.io/github/ancapdev/PcapTools.jl) diff --git a/scripts/packet-analysis/dev/PcapTools/src/PcapTools.jl b/scripts/packet-analysis/dev/PcapTools/src/PcapTools.jl new file mode 100644 index 0000000..2299d36 --- /dev/null +++ b/scripts/packet-analysis/dev/PcapTools/src/PcapTools.jl @@ -0,0 +1,26 @@ +module PcapTools + +using Dates +using Mmap +using UnixTimes +using UnsafeArrays + +export PcapHeader, RecordHeader +export PcapRecord +export PcapReader, PcapStreamReader, PcapBufferReader +export PcapWriter, PcapStreamWriter +export LINKTYPE_NULL, LINKTYPE_ETHERNET +export splitcap + +abstract type PcapReader end +abstract type PcapWriter end + +include("pcap_header.jl") +include("record_header.jl") +include("record.jl") +include("buffer_reader.jl") +include("stream_reader.jl") +include("stream_writer.jl") +include("splitcap.jl") + +end diff --git a/scripts/packet-analysis/dev/PcapTools/src/buffer_reader.jl b/scripts/packet-analysis/dev/PcapTools/src/buffer_reader.jl new file mode 100644 index 0000000..25211f8 --- /dev/null +++ b/scripts/packet-analysis/dev/PcapTools/src/buffer_reader.jl @@ -0,0 +1,96 @@ +""" +Reads pcap data from an array of bytes. +""" +mutable struct PcapBufferReader <: PcapReader + data::Vector{UInt8} + raw_header::Vector{UInt8} + header::PcapHeader + offset::Int64 + mark::Int64 + usec_mul::Int64 + bswapped::Bool + + @doc """ + PcapBufferReader(data::Vector{UInt8}) + + Create reader over `data`. Will read and process pcap header, + and yield records through `read(::PcapBufferReader)`. + """ + function PcapBufferReader(data::Vector{UInt8}) + length(data) < sizeof(PcapHeader) && throw(EOFError()) + rh = data[1:sizeof(PcapHeader)] + h = unsafe_load(Ptr{PcapHeader}(pointer(data))) + h, bswapped, nanotime = process_header(h) + new(data, rh, h, sizeof(h), -1, nanotime ? 1 : 1000, bswapped) + end +end + +""" + PcapBufferReader(path::AbstractString) + +Memory map file in `path` and create PcapBufferReader over its content. +""" +function PcapBufferReader(path::AbstractString) + io = open(path) + data = Mmap.mmap(io) + PcapBufferReader(data) +end + +function Base.close(x::PcapBufferReader) + x.data = UInt8[] + x.offset = 0 + nothing +end + +Base.length(x::PcapBufferReader) = length(x.data) +Base.position(x::PcapBufferReader) = x.offset; nothing +Base.seek(x::PcapBufferReader, pos) = x.offset = pos; nothing + +function Base.mark(x::PcapBufferReader) + x.mark = x.offset + x.mark +end + +function Base.unmark(x::PcapBufferReader) + if x.mark >= 0 + x.mark = -1 + true + else + false + end +end + +Base.ismarked(x::PcapBufferReader) = x.mark >= 0 + +function Base.reset(x::PcapBufferReader) + !ismarked(x) && error("PcapBufferReader not marked") + x.offset = x.mark + x.mark = -1 + x.offset +end + +Base.eof(x::PcapBufferReader) = (length(x) - x.offset) < sizeof(RecordHeader) + +""" + read(x::PcapBufferReader) -> PcapRecord + +Read one record from pcap data. +Throws `EOFError` if no more data available. +""" +@inline function Base.read(x::PcapBufferReader) + eof(x) && throw(EOFError()) + record_offset = x.offset + p = pointer(x.data) + record_offset + GC.@preserve x begin + h = unsafe_load(Ptr{RecordHeader}(p)) + end + if x.bswapped + h = bswap(h) + end + t1 = (h.ts_sec + x.header.thiszone) * 1_000_000_000 + t2 = Int64(h.ts_usec) * x.usec_mul + t = UnixTime(Dates.UTInstant(Nanosecond(t1 + t2))) + x.offset += sizeof(RecordHeader) + h.incl_len + x.offset > length(x) && error("Insufficient data in pcap record") + PcapRecord(h, t, x.data, record_offset) +end diff --git a/scripts/packet-analysis/dev/PcapTools/src/pcap_header.jl b/scripts/packet-analysis/dev/PcapTools/src/pcap_header.jl new file mode 100644 index 0000000..cedd49e --- /dev/null +++ b/scripts/packet-analysis/dev/PcapTools/src/pcap_header.jl @@ -0,0 +1,46 @@ +# NOTE: Not using @enum because it craps out when displaying unknown values +const LINKTYPE_NULL = UInt32(0) +const LINKTYPE_ETHERNET = UInt32(1) + +struct PcapHeader + magic::UInt32 + version_major::UInt16 + version_minor::UInt16 + thiszone::Int32 + sigfigs::UInt32 + snaplen::UInt32 + linktype::UInt32 +end + +function Base.bswap(x::PcapHeader) + PcapHeader( + bswap(x.magic), + bswap(x.version_major), + bswap(x.version_minor), + bswap(x.thiszone), + bswap(x.sigfigs), + bswap(x.snaplen), + bswap(x.linktype)) +end + +function process_header(x::PcapHeader) + if x.magic == 0xa1b2c3d4 + bswapped = false + nanotime = false + elseif x.magic == 0xd4c3b2a1 + bswapped = true + nanotime = false + elseif x.magic == 0xa1b23c4d + bswapped = false + nanotime = true + elseif x.magic == 0x4d3cb2a1 + bswapped = true + nanotime = true + else + throw(ArgumentError("Invalid pcap header")) + end + if bswapped + x = bswap(x) + end + x, bswapped, nanotime +end diff --git a/scripts/packet-analysis/dev/PcapTools/src/record.jl b/scripts/packet-analysis/dev/PcapTools/src/record.jl new file mode 100644 index 0000000..45d3be1 --- /dev/null +++ b/scripts/packet-analysis/dev/PcapTools/src/record.jl @@ -0,0 +1,25 @@ +""" +Record of pcap data +""" +struct PcapRecord + header::RecordHeader + timestamp::UnixTime + underlying_data::Vector{UInt8} + record_offset::Int +end + +@inline function record_field_(x::PcapRecord, ::Val{:data}) + offset = getfield(x, :record_offset) + sizeof(RecordHeader) + len = Int(getfield(x, :header).incl_len) + UnsafeArray{UInt8, 1}(pointer(getfield(x, :underlying_data)) + offset, (len,)) +end + +@inline function record_field_(x::PcapRecord, ::Val{:raw}) + offset = getfield(x, :record_offset) + len = sizeof(RecordHeader) + getfield(x, :header).incl_len + UnsafeArray{UInt8, 1}(pointer(getfield(x, :underlying_data)) + offset, (len,)) +end + +@inline record_field_(x::PcapRecord, ::Val{f}) where {f} = getfield(x, f) + +@inline Base.getproperty(x::PcapRecord, f::Symbol) = record_field_(x, Val(f)) diff --git a/scripts/packet-analysis/dev/PcapTools/src/record_header.jl b/scripts/packet-analysis/dev/PcapTools/src/record_header.jl new file mode 100644 index 0000000..9aabce9 --- /dev/null +++ b/scripts/packet-analysis/dev/PcapTools/src/record_header.jl @@ -0,0 +1,14 @@ +struct RecordHeader + ts_sec::UInt32 + ts_usec::UInt32 + incl_len::UInt32 + orig_len::UInt32 +end + +function Base.bswap(x::RecordHeader) + RecordHeader( + bswap(x.ts_sec), + bswap(x.ts_usec), + bswap(x.incl_len), + bswap(x.orig_len)) +end diff --git a/scripts/packet-analysis/dev/PcapTools/src/splitcap.jl b/scripts/packet-analysis/dev/PcapTools/src/splitcap.jl new file mode 100644 index 0000000..68c4041 --- /dev/null +++ b/scripts/packet-analysis/dev/PcapTools/src/splitcap.jl @@ -0,0 +1,102 @@ +strip_nothing_(::Type{Union{Nothing, T}}) where T = T +strip_nothing_(::Type{T}) where T = T + +progress_noop_(n) = nothing + +mutable struct SplitCapOutput{S} + work_buffer::Vector{UInt8} + complete_buffers::Channel{Vector{UInt8}} + stream::S +end + +# Since Julia (as of 1.5) doesn't support task migration, +# continue with new task after each buffer write, to rebalance across threads +function write_one_and_continue_(output::SplitCapOutput, free_buffers::Channel, pending::Threads.Atomic{Int}) + i = iterate(output.complete_buffers) + if i === nothing + Threads.atomic_sub!(pending, 1) + return nothing + end + b, _ = i + write(output.stream, b) + empty!(b) + put!(free_buffers, b) + Threads.@spawn write_one_and_continue_($output, $free_buffers, $pending) +end + +function splitcap( + ::Type{KeyType}, + ::Type{StreamType}, + reader::PcapReader, + record2key, + key2stream, + progress_callback = progress_noop_; + own_streams::Bool = true +) where {KeyType, StreamType} + buffer_size = 1024 * 1024 * 2 + max_pending_buffers = 4 + outputs = Dict{KeyType, SplitCapOutput{StreamType}}() + free_buffers = Channel{Vector{UInt8}}(Inf) + n = 0 + pending = Threads.Atomic{Int}(0) + try + while !eof(reader) + record = read(reader) + dst = record2key(record) + if dst isa KeyType + output = get!(outputs, dst) do + stream = key2stream(dst) + buffer = sizehint!(UInt8[], buffer_size + 1500) + own_streams && append!(buffer, reader.raw_header) + output = SplitCapOutput{StreamType}( + buffer, + Channel{Vector{UInt8}}(max_pending_buffers), + stream) + Threads.atomic_add!(pending, 1) + Threads.@spawn write_one_and_continue_($output, $free_buffers, $pending) + output + end + append!(output.work_buffer, record.raw) + if length(output.work_buffer) >= buffer_size + put!(output.complete_buffers, output.work_buffer) + if isready(free_buffers) + output.work_buffer = take!(free_buffers) + else + output.work_buffer = sizehint!(UInt8[], buffer_size + 1500) + end + end + end + n += 1 + progress_callback(n) + GC.safepoint() + end + for output in values(outputs) + if !isempty(output.work_buffer) + put!(output.complete_buffers, output.work_buffer) + end + close(output.complete_buffers) + end + while pending[] != 0 + sleep(0.1) + end + finally + if own_streams + for output in values(outputs) + close(output.stream) + end + end + end + nothing +end + +function splitcap( + reader::PcapReader, + record2key, + key2stream, + progress_callback = progress_noop_; + kwargs... +) + KeyType = strip_nothing_(Core.Compiler.return_type(record2key, Tuple{PcapRecord})) + StreamType = Core.Compiler.return_type(key2stream, Tuple{KeyType}) + splitcap(KeyType, StreamType, reader, record2key, key2stream, progress_callback; kwargs...) +end diff --git a/scripts/packet-analysis/dev/PcapTools/src/stream_reader.jl b/scripts/packet-analysis/dev/PcapTools/src/stream_reader.jl new file mode 100644 index 0000000..ef9089d --- /dev/null +++ b/scripts/packet-analysis/dev/PcapTools/src/stream_reader.jl @@ -0,0 +1,63 @@ +""" +Reads pcap data from a stream. +""" +mutable struct PcapStreamReader{Src <: IO} <: PcapReader + src::Src + raw_header::Vector{UInt8} + header::PcapHeader + usec_mul::Int64 + bswapped::Bool + record_buffer::Vector{UInt8} + + @doc """ + PcapStreamReader(src::IO) + + Create reader over `src`. Will read and process pcap header, + and yield records through `read(::PcapStreamReader)`. + """ + function PcapStreamReader(src::Src) where {Src <: IO} + raw_header = read(src, sizeof(PcapHeader)) + length(raw_header) != sizeof(PcapHeader) && throw(EOFError()) + h = GC.@preserve raw_header unsafe_load(Ptr{PcapHeader}(pointer(raw_header))) + header, bswapped, nanotime = process_header(h) + new{Src}(src, raw_header, header, nanotime ? 1 : 1000, bswapped, zeros(UInt8, 9000 + sizeof(RecordHeader))) + end +end + +""" + PcapStreamReader(path) + +Open file at `path` and create PcapStreamReader over its content. +""" +PcapStreamReader(path::AbstractString) = PcapStreamReader(open(path)) + +Base.close(x::PcapStreamReader) = close(x.src) +Base.position(x::PcapStreamReader) = position(x.src) +Base.seek(x::PcapStreamReader, pos) = seek(x.src, pos) +Base.mark(x::PcapStreamReader) = mark(x.src) +Base.unmark(x::PcapStreamReader) = unmark(x.src) +Base.ismarked(x::PcapStreamReader) = ismarked(x.src) +Base.reset(x::PcapStreamReader) = reset(x.src) +Base.eof(x::PcapStreamReader) = eof(x.src) + +""" + read(x::PcapStreamReader) -> PcapRecord + +Read one record from pcap data. Record is valid until next read(). +Throws `EOFError` if no more data available. +""" +function Base.read(x::PcapStreamReader) + p = pointer(x.record_buffer) + GC.@preserve x begin + unsafe_read(x.src, p, sizeof(RecordHeader)) + h = unsafe_load(Ptr{RecordHeader}(p)) + if x.bswapped + h = bswap(h) + end + unsafe_read(x.src, p + sizeof(RecordHeader), h.incl_len) + end + t1 = (h.ts_sec + x.header.thiszone) * 1_000_000_000 + t2 = Int64(h.ts_usec) * x.usec_mul + t = UnixTime(Dates.UTInstant(Nanosecond(t1 + t2))) + PcapRecord(h, t, x.record_buffer, 0) +end diff --git a/scripts/packet-analysis/dev/PcapTools/src/stream_writer.jl b/scripts/packet-analysis/dev/PcapTools/src/stream_writer.jl new file mode 100644 index 0000000..3c2b6c7 --- /dev/null +++ b/scripts/packet-analysis/dev/PcapTools/src/stream_writer.jl @@ -0,0 +1,30 @@ +struct PcapStreamWriter{Dst <: IO} <: PcapWriter + dst::Dst + + function PcapStreamWriter{Dst}(dst::Dst; thiszone = 0, snaplen = 65535, linktype = LINKTYPE_ETHERNET) where {Dst <: IO} + h = PcapHeader( + 0xa1b23c4d, + 0x0002, + 0x0004, + thiszone, + 0, + snaplen, + linktype) + write(dst, reinterpret(UInt8, [h])) + new(dst) + end +end + +PcapStreamWriter(io::IO; kwargs...) = PcapStreamWriter{typeof(io)}(io; kwargs...) +PcapStreamWriter(path::AbstractString; kwargs...) = PcapStreamWriter(open(path, "w"); kwargs...) + +Base.close(x::PcapStreamWriter) = close(x.dst) + +function Base.write(x::PcapStreamWriter, timestamp::UnixTime, data) + sec, nsec = fldmod(Dates.value(timestamp), 1_000_000_000) + data_length = length(data) + h = RecordHeader(sec, nsec, data_length, data_length) + write(x.dst, reinterpret(UInt8, [h])) + write(x.dst, collect(data)) + nothing +end diff --git a/scripts/packet-analysis/msgdumps/CQ-TS7471LC_bootup.pcap b/scripts/packet-analysis/msgdumps/CQ-TS7471LC_bootup.pcap new file mode 100644 index 0000000000000000000000000000000000000000..84a191621c3131a85ffaa955b88e5f05827146eb GIT binary patch literal 706 zcmZ`$Jxjzu5S>ZR6LWVaw}*%66pWRixlXVMc-pA}K@J27@dFFB3fO9Kt)#N|dK*Eo z^cQGr8T|$EY=t7+`GHzzGsQeN6AKG-+p$9cJcK&;4cwAbxyX6|^%DW8xYiach`m z8Qjoio>4abs-(h1Q8on(Am6|gfI~DoCv1A`5it13nkRDA4ob*0| z-DnT;d2st>^`yBu5;CNEmegZ_TPfx1{BdpC8FFd{2-Ixo*6kKoczOTr76vYGTQL@8 zFr@zsgieaCh55f?Ws4|=}LXIp{k40h2*-r~m)} literal 0 HcmV?d00001 diff --git a/scripts/packet-analysis/msgdumps/amp-settings.pcapng b/scripts/packet-analysis/msgdumps/amp-settings.pcapng new file mode 100644 index 0000000000000000000000000000000000000000..9e77450e3b294fa3bb89b6fed0f55a62ae64797f GIT binary patch literal 8812 zcmbW632YQq7=YiFa+K|!y#h^iP=PJ9O}jm|c!ZI*P>uo?1fw85SjyUiW!t43QU^_u z)&K=D7==g?hz6}30)>i<7Yd=K2o_OM{E zf+*M%Gm^&TEEQ~3uFCnsv}xI?iS{_u2SuYvu5#~UAwAKan3Rx`mQd_0cY0h!2!*0N zl;tgR7Ztb#+f0|oSuwxBQxYenCMG4O3$}5tN?}pYXLmtSNkOr5u24|X^KU|OwR5pM zx!POkEwA+2m5~*}YPY)}IZiQLIj|qthb-mXs+^*7hA6)zDAzFM8m3&sIkqW(6Skjn z&A?xjqqJdtjCg!fOO)~%pGa4)>oyrHlW)`)o6Tmc8JX~}L>_bT+6L)<^ro z0$gWxWwQh63q&@)seqv)!oTO!SP*Vq&2tPclE=w|*Q{i4QT`mAZ&)8Hd~tO!^9?g{ z$R#9J5}CzSqvBGdN~_5^l)C2N|4dKG6P!Ir)K= zs{-j?SXSCm{~k++#c$6_=MK@4w2C#ytyVvd_94Qhd76UhLzody3}mcRW^06>E?*nP z2?Zape2e68sPKJOKT+zm_decNpIj4m^=|Y@O%cDcIG%s};5Mi^cJB zF3!62H;d!;g_6$OEY8G;=Rzg#D0Be1m%^<7{gjcz$=<`EEPVzJhtR%$EH0eK59=Tj;Bv9t$mcm@%)pTf68NVym*Re9%GMhw({~Pg-vAapVj*>TAw)Y^6_y? zS$4AOlW@tIjD4_LAMKZ<&X%v;%j=i4e_>g%weDgt{mX;vxcHnK^v9yMi^c(S(#h|Z zgPfJmp<7wcgIolklLwWq2f0W-hXyU*2y#(;PCgU59ps|<9BR_T&n05i9L+P1r}*`& zKk)M0EA~|{b=-|@(zhev-l`D`@yt=Uw{l`%fqjad6$0AH$u;Jr#(KC%a%u~6@`RB7 zpgy3M>AWHOY#WTh%p1(0Bk?9s7w|ld=8ot>x$wTwq^=KNPV5+SKd8^B!98(dD99Q3 zoOE{6aF7erLr0vAF$syKl1Pct3gg9USij}%^;_dAF;8|+aRY=4=DdOyzkVgKPkQK1gH^IC;m9jfcu*W zK$owL()Tr4kd2WV9O~Q+K5LEA;N+5G@L6lL1}8pKI}@@qMuU@Ix%>viY3Y;R&DaWY zTK-90v$sK+VTG|oEcyFNYbH4h}f^(li z{+YD$*Kh@VZe-NrM*nyU(r3`(b~d*`T(|}&ePTQdae9EG>kFCd5!vVaE9(h9X$1Y( z7c3`@opKQxKa4ZJSK-hfhX-qokL9Ee`#sS3ko`2Tm=jCC0@qb2UbO#WPOjOR8c6>o zK1`VmuC2UW65F!xdj#@{S3ATu@0=+R$E#JMZ{Cx60H<6U zw|#V8C7XTcT{)f4SQhPXhwsIJK0{->6vqj5nuGCSnZk%Ge$fYF7m+n~!S@*)yD%q5 zw?7oDT~bQsk#J?6;!~%q?~`xpi!3@}3yvwCyEL}X;_+ic9D(>iWZ#Mg`wFja370mq ISj&fh0lDIWN&o-= literal 0 HcmV?d00001 diff --git a/scripts/packet-analysis/msgdumps/avclan.pcap b/scripts/packet-analysis/msgdumps/avclan.pcap new file mode 100644 index 0000000000000000000000000000000000000000..6db1a00cee59dbfa72c7a24bb5257e207bf4f381 GIT binary patch literal 26358 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 literal 0 HcmV?d00001 diff --git a/scripts/packet-analysis/msgdumps/cd-insertion-functions-eject.pcapng b/scripts/packet-analysis/msgdumps/cd-insertion-functions-eject.pcapng new file mode 100644 index 0000000000000000000000000000000000000000..4692028d2b75fa71ba02129f2e5da33ef3b01e8c GIT binary patch literal 9708 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 X7*=zuAJLai=CjjMHLGsA}@DdGF literal 0 HcmV?d00001 diff --git a/scripts/packet-analysis/msgdumps/lan-test-bus-scan.pcap b/scripts/packet-analysis/msgdumps/lan-test-bus-scan.pcap new file mode 100644 index 0000000000000000000000000000000000000000..b21835f2c6bf9763a456df60f62b27ecd1711c9f GIT binary patch literal 350 zcmeaZ*|d;}frSAE9Ds~PKnxPu@hOsv3rK@7BVz*pe|}~LMlO)diJH&6JU|+R8JG%y za;yxD3{s3BxrUEtq~UUm6Hw$FL{`aj1J!^q19JjUjs>Jo1;{I?&X&Qjj0YqmaEDz1 zZXnz;83t9LT)^uoOmI24We~X=FJ6gbn8*Rrwc=E(G~5|r6G6_907+e#EvgBZ0viaH g(gaFb-1ZlR+XZ$!$kQ?mAh{P)k7-z`)>ZqbzlQMRUniJpO;nSzFUW{E->gk6xBoSm4Snxc?c z43X6}tV^vZFsv(0D$Oe?H3FIpR9II~kZ7n0bT|-z`~mWV3lJ{?Vh12*0*dGYX-*)` z38Z=OxD6-`*2@N@bwC^-FmLY>cc@Cg#RX)8FazTR{{Q^Uf*^Z9YS7g-7*4p$0~7~g zM&<;dYE}kDL4jm+wH82g8v+)KV3-Ru7^~VD-D2E8GeMY7fK6vtn_=F;oE-1(XC5ln*M7Ws@xSlEkr$?a(4c zi$r@OQWT(vfP@I&w;l=yM8&phDnLl7)Jp||UfO~HArPb&4(YrJ(~jAPy}QzAKm2>| z{r_)fy}Ju4Di-`o2x)8hq());nYUXRiIbL8*60s*hG^qp#t80d3sEy3j>jXB<__A} z{yEiXOx+l1YW|7_hs@&& z+(5cVPt)LDGh-Y~>Y2U}U9U#edKzppvot?@oYs5#^n}q%^@Fp&hNHvAp>%XO*PTmc zb1{~gfy3#v9t|!=?0zIS1aZ{!v}T z#KTaO(K-A4ACO9gUIYgEBY4N79a%S?fwg{V9rNlL;SKmT-bng1V!`E--BRqP40&h*}yWDu@?B;&ct7jf}^2!LJ zsbC(2XW;GVicyqj4_e^l=PmHsgE#-6JhG1Zfgj0lMR_T|SQo(Cb$xj^J>t1*PMq-C zBZwohN8h5FBo9K<0BG85oGqqFX0_VC&pFxu-&aNNAuoB>jyylu(?HT8?gQX8?cRB( zU;MpIuA_DCx#0B6J{8_m$J$ms>8yjl=M{KAw(^0)JH6z1XXQQJb@H!LnuLcGc%OK^ m6yf 0x0, 32)) + +function Base.tryparse(::Type{AVCLANframe}, str::String) + vals = split(str) + + broadcast = tryparse(Bool, vals[1]) + isnothing(broadcast) && return nothing + + controller_addr = tryparse(UInt16, vals[2]) + isnothing(controller_addr) && return nothing + + peripheral_addr = tryparse(UInt16, vals[3]) + isnothing(peripheral_addr) && return nothing + + control = tryparse(UInt8, vals[4]) + isnothing(control) && return nothing + + len = tryparse(UInt8, vals[5]) + isnothing(len) && return nothing + + if (length(vals) - 5) != len || len > 32 + return nothing + end + _data = tryparse.(UInt8, vals[6:end]) + data = ntuple(i -> checkindex(Bool, axes(_data, 1), i) ? _data[i] : 0x0, 32) + + return AVCLANframe(broadcast, controller_addr, peripheral_addr, control, len, data) +end + +function tobytes(frame::AVCLANframe) + data = Vector{UInt8}(undef, 0) + push!(data, frame.broadcast) + append!(data, reverse(reinterpret(reshape, UInt8, [frame.controller_addr])), + reverse(reinterpret(reshape, UInt8, [frame.peripheral_addr])), + reinterpret(reshape, UInt8, [frame.control]), + reinterpret(reshape, UInt8, [frame.length]), + frame.data[1:frame.length]) + + return data +end + +function avclan_text_to_pcap(textlog::String, pcap_fn::String) + pcapstream = PcapStreamWriter(pcap_fn; snaplen=64, linktype = 162) + t = UnixTime(now()) + + for line in eachline(textlog) + frame = tryparse(AVCLANframe, line) + t += Microsecond(rand(1:15)) + if !isnothing(frame) + write(pcapstream, t, tobytes(frame)) + end + end + close(pcapstream) + + return pcap_fn +end + +end # module AVCLANPipe diff --git a/scripts/packet-analysis/src/convert-sr-annotation-export.jl b/scripts/packet-analysis/src/convert-sr-annotation-export.jl new file mode 100644 index 0000000..71dd4e8 --- /dev/null +++ b/scripts/packet-analysis/src/convert-sr-annotation-export.jl @@ -0,0 +1,55 @@ +using AVCLANPipe, PcapTools, Dates, UnixTimes + +file = ARGS[1] +outfile = ARGS[2] + +function convert_fields_to_pcap(file, outfile) + + io = open(file, "r") + outio = PcapStreamWriter(outfile; snaplen=64, linktype = 162) + + packet = AVCLANframe() + packetcomplete = false + local t::UnixTime + i = 1 + + for line in readlines(io) + m = match(r"(\d+)-(\d+) IEBus: Raw Fields: (?\w+)(:? ((?0x[0-9a-f]{2,3})|WRITE_DATA|Length: (?\d+)))?", + line) + + if m[:field] == "Broadcast" + packet.broadcast = true + t = UnixTime(1970,1,1,0,0,0,0, something(tryparse(Int, m[1]), 0)) + elseif m[:field] == "Unicast" + packet.broadcast = false + t = UnixTime(1970,1,1,0,0,0,0, something(tryparse(Int, m[1]), 0)) + elseif m[:field] == "Master" + packet.controller_addr = tryparse(UInt16, m[:hex]) + elseif m[:field] == "Slave" + packet.peripheral_addr = tryparse(UInt16, m[:hex]) + elseif m[:field] == "Data" + if !isnothing(m[:length]) + packet.length = convert(UInt8, tryparse(Int, m[:length])) + else + data = collect(packet.data) + data[i] = tryparse(UInt8, m[:hex]) + packet.data = ntuple(n -> data[n], 32) + i += 1 + end + end + + if i > packet.length + packetcomplete = true + end + + if packetcomplete + packetcomplete = false + i = 1 + write(outio, t, tobytes(packet)) + end + end + + close(io) + close(outio) + +end diff --git a/scripts/packet-analysis/src/pipepackets.jl b/scripts/packet-analysis/src/pipepackets.jl new file mode 100644 index 0000000..66c7d53 --- /dev/null +++ b/scripts/packet-analysis/src/pipepackets.jl @@ -0,0 +1,45 @@ +include("AVCLANPipe.jl") + +using .AVCLANPipe, PcapTools, Dates, UnixTimes, LibSerialPort + +serial_port="/dev/ttyUSB0" +baud=1200000 + +pcapstream = PcapStreamWriter(stdout; snaplen=64, linktype=162) +serial = LibSerialPort.open(serial_port, baud) +set_flow_control(serial) # Disable flow-control (stops it from eating raw byte 0x11) + +write(serial, 'X') +buf = IOBuffer() + +function quit(serialio=serial) + close(serialio) + exit() +end + +while true + iswritable(stdout) || isopen(stdout) || quit() + if bytesavailable(serial) > 0 + t = UnixTime(now()) + line = readline(serial) + # allthebytes = Vector{UInt8}(undef, bytesavailable(serial)) + # readbytes!(serial, allthebytes) + # write(buf, allthebytes) + # l = findfirst(==('\n'), buf.data) + # if !isnothing(l) + # println(stderr, "fucku") + # bytes = Vector{UInt8}(undef, l) + # seekstart(buf) + # readbytes!(buf, bytes) + # seekend(buf) + bytes = Vector{UInt8}(line) + if bytes[1] == 0x10 && bytes[end] == 0x17 + write(pcapstream, t, bytes[2:end-1]) + else + @error line + end + # end + # println(stderr, "wtf") + end + yield() +end