Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CP State diagnostics (HomePlug AV packets) #12

Merged
merged 10 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Images/HPAV_LLC_AC.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 15 additions & 8 deletions Installer/InstallerScript.iss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// DO NOT CHANGE VERSION HERE! Run update_version.bat
#define AppVer "1.3.1"
#define AppVer "1.4.0"
#define AppId "dsV2Gshark"

[Setup]
Expand Down Expand Up @@ -43,18 +43,20 @@ Name: "plugin/decoder"; Description: "EXI decoder (powered by chargebyte cbexige
Name: "plugin/decoder/din"; Description: "DIN 70121 support"; Types: full custom; Flags: fixed
Name: "plugin/decoder/iso2"; Description: "ISO 15118-2 support"; Types: full custom; Flags: fixed
Name: "plugin/decoder/iso20"; Description: "ISO 15118-20 support (experimental)"; Types: full custom; Flags: fixed
Name: "plugin/autoschema"; Description: "Automatic schema detection"; Types: full custom; Flags: fixed
Name: "plugin/autoschema"; Description: "Automatic schema detection"; Types: full custom; Flags: fixed
Name: "plugin/autodecrypt"; Description: "Live TLS decryption with disclosed master secret from UDP packet"; Types: full custom;
Name: "plugin/llc_diagnostic"; Description: "Additional dissector for CP-State related Homeplug AV packets"; Types: full custom;
Name: "buttons"; Description: "Add filter buttons to Wireshark (current user only)"; Types: full
Name: "colorfilters"; Description: "Highlight V2G messages in Wireshark (current user only)"; Types: full
Name: "iograph"; Description: "Prepare Wireshark I/O Graphs for V2G messages{cm:Linebreak}(current user only, may override I/O Graph preferences)"; Types: full

[Files]
Source: "..\Wireshark\plugins\v2gshared.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/dissectors
Source: "..\Wireshark\plugins\v2gcommon.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/dissectors
Source: "..\Wireshark\plugins\v2gmsg.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/dissectors
Source: "..\Wireshark\plugins\v2gtp.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/dissectors
Source: "..\Wireshark\plugins\v2gsdp.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/dissectors
Source: "..\Wireshark\plugins\v2gtlssecret.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/autodecrypt
Source: "..\Wireshark\plugins\v2gllc.lua"; DestDir: "{app}\plugins"; Flags: ignoreversion recursesubdirs; Components: plugin/llc_diagnostic
Source: "..\Wireshark\*.dll"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: plugin/decoder
Source: "..\LICENSE"; DestDir: "{app}"; DestName: "dsV2Gshark_LICENSE.txt"; Flags: ignoreversion;
Source: "..\OSSAcknowledgements.txt"; DestDir: "{app}"; DestName: "dsV2Gshark_OSSAcknowledgements.txt"; Flags: ignoreversion recursesubdirs;
Expand All @@ -64,6 +66,7 @@ Source: "dsV2Gshark_README.txt"; DestDir: "{app}"; DestName: "dsV2Gshark_README.
Type: filesandordirs; Name: "{app}\luaV2Gdecoder.dll"
Type: filesandordirs; Name: "{app}\X509CertInfos.dll"
Type: filesandordirs; Name: "{app}\plugins\v2gmsg_generic.lua"
Type: filesandordirs; Name: "{app}\plugins\v2gshared.lua"

[Code]
function HasWriteAccessToApp: Boolean;
Expand Down Expand Up @@ -212,10 +215,10 @@ var
i: Integer;
begin
// check version of lua files
StringVersionPrefix := 'v2gshared.DS_V2GSHARK_VERSION = "';
StringVersionPrefix := 'v2gcommon.DS_V2GSHARK_VERSION = "';
stringVersionSuffix := '" -- DO NOT CHANGE';
ExtractTemporaryFile('v2gshared.lua');
if LoadStringsFromFile(ExpandConstant('{tmp}\v2gshared.lua'), Lines) then
ExtractTemporaryFile('v2gcommon.lua');
if LoadStringsFromFile(ExpandConstant('{tmp}\v2gcommon.lua'), Lines) then
begin
for i := 0 to GetArrayLength(Lines) - 1 do
begin
Expand Down Expand Up @@ -271,7 +274,7 @@ procedure CurStepChanged(CurStep: TSetupStep);
var
FileName: string;
Lines: TArrayOfString;
StringsToCheck, StringsToAdd: TArrayOfString;
StringsToCheck, StringsToAdd, StringsToRemove: TArrayOfString;
begin
// add wireshark filter buttons after installation
if (CurStep = ssPostInstall) then
Expand Down Expand Up @@ -335,9 +338,13 @@ begin
'"Disabled","[ISO20] Max Current EV","","#2E3436","Line","AVG(Y Field)","v2gmsg.xml.iograph.EVMaximumChargeCurrent","None","1"',
'"Disabled","[ISO20] Max Voltage EVSE","","#2E3436","Line","AVG(Y Field)","v2gmsg.xml.iograph.EVSEMaximumVoltage","None","1"',
'"Disabled","[ISO20] Max Current EVSE","","#2E3436","Line","AVG(Y Field)","v2gmsg.xml.iograph.EVSEMaximumChargeCurrent","None","1"',
'"Disabled","CP State","","#2E3436","Dot","AVG(Y Field)","homeplug_av.st_iotecha.cpstate.state","None","20"'];
'"Disabled","CP State","","#2E3436","Dot","AVG(Y Field)","homeplug-av-llc.cpstate","None","20"',
'"Disabled","(disabled_filter_button1)","v2gtp or v2gtlssecret or tls.handshake or tls.alert_message or tls.change_cipher_spec or tcp.flags.syn == 1 or tcp.flags.fin == 1 or homeplug or homeplug-av ","#FFFFFF","Line","Packets","","None","1"',
'"Disabled","(disabled_filter_button2)","v2gtp or v2gtlssecret","#FFFFFF","Line","Packets","","None","1"'];
StringsToRemove := ['"Disabled","CP State","","#2E3436","Dot","AVG(Y Field)","homeplug_av.st_iotecha.cpstate.state","None","20"'];
if FileExists(FileName) then
begin
RemoveFromFile(FileName, StringsToRemove);
if not PrependStringsToFile(FileName, StringsToAdd) then
MsgBox('Failed to add I/O Graph presets to Wireshark!', mbError, MB_OK);
end
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This Wireshark plugin allows to analyze and decode packets between electric vehi
- Validation of V2G messages according to XSD specification
- Certificate information details for Plug & Charge (PnC)
- Live TLS decryption
- LLC diagnostics via HomePlug AV packets for sniffer and debug packets
- Automatic schema detection
- Detect schema automatically in case of missing SDP or SAP
- Color filter for V2G packets
Expand Down Expand Up @@ -91,3 +92,5 @@ Click on a packet in the graph to inspect it in the Wireshark main window. Press
![Plugin Preferences](Images/WS_Preferences.png)
### Wireshark I/O Graph
![I/O Graph](Images/IO_Graph.png)
### HomePlug AV LLC Diagnostics
![I/O Graph](Images/HPAV_LLC_AC.png)
8 changes: 4 additions & 4 deletions V2G_Libraries/CertificateInfos/main.rc
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#include <windows.h>

#define VER_FILEVERSION 1,3,1,0
#define VER_FILEVERSION_STR "1.3.1.0\0"
#define VER_FILEVERSION 1,4,0,0
#define VER_FILEVERSION_STR "1.4.0.0\0"
#define VER_COMPANYNAME_STR "dSPACE GmbH"
#define VER_PRODUCTNAME_STR "V2gCertificateInfos"
#define VER_PRODUCTVERSION 1,3,1,0
#define VER_PRODUCTVERSION_STR "1.3.1.0\0"
#define VER_PRODUCTVERSION 1,4,0,0
#define VER_PRODUCTVERSION_STR "1.4.0.0\0"

VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
Expand Down
8 changes: 4 additions & 4 deletions V2G_Libraries/V2GDecoder/main.rc
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#include <windows.h>

#define VER_FILEVERSION 1,3,1,0
#define VER_FILEVERSION_STR "1.3.1.0\0"
#define VER_FILEVERSION 1,4,0,0
#define VER_FILEVERSION_STR "1.4.0.0\0"
#define VER_COMPANYNAME_STR "dSPACE GmbH"
#define VER_PRODUCTNAME_STR "V2gDecoder"
#define VER_PRODUCTVERSION 1,3,1,0
#define VER_PRODUCTVERSION_STR "1.3.1.0\0"
#define VER_PRODUCTVERSION 1,4,0,0
#define VER_PRODUCTVERSION_STR "1.4.0.0\0"

VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
Expand Down
33 changes: 33 additions & 0 deletions Wireshark/plugins/v2gcommon.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--
-- Copyright 2024, dSPACE GmbH. All rights reserved.
--
-- See license file (dsV2Gshark_LICENSE.txt)
--

-- do OS specific stuff, required to properly load v2g libs
local plugins_path -- path to the plugins directory of this script
local lib_pattern
if package.config:sub(1, 1) == "\\" then
-- WINDOWS
plugins_path = debug.getinfo(1, "S").source:sub(2):match("(.*[/\\])") or "./"
lib_pattern = "?.dll"
else
-- UNIX
plugins_path = debug.getinfo(1, "S").source:sub(2):match("(.*/)") or "./"
lib_pattern = "?.so"
end
local wireshark_path = plugins_path .. "../"
if not string.find(plugins_path, package.path) then
-- extend path (where to load .lua files)
package.path = package.path .. ";" .. plugins_path .. "?.lua"
end
if not string.find(wireshark_path, package.cpath) then
-- extend cpath (where to load .so files)
package.cpath = package.cpath .. ";" .. wireshark_path .. lib_pattern .. ";" .. plugins_path .. lib_pattern
end

local v2gcommon = {}

v2gcommon.DS_V2GSHARK_VERSION = "1.4.0" -- DO NOT CHANGE

return v2gcommon
183 changes: 183 additions & 0 deletions Wireshark/plugins/v2gllc.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
--
-- Copyright 2024, dSPACE GmbH. All rights reserved.
--
-- This dissector adds functionality to the standard HomePlug AV dissector
-- to allow displaying CP State indication packets
--
-- See license file (dsV2Gshark_LICENSE.txt)
--
local v2gcommon = require("v2gcommon")

p_hpav_llc = Proto("homeplug-av-llc", "HomePlug AV protocol LLC diagnostics")
local p_hpav_llc_info = {
version = v2gcommon.DS_V2GSHARK_VERSION,
author = "dSPACE GmbH",
repository = "https://github.com/dspace-group/dsV2Gshark"
}
set_plugin_info(p_hpav_llc_info)

local f_freq = ProtoField.int16("homeplug-av-llc.freq", "Frequency", base.DEC)
local f_dutycycle = ProtoField.float("homeplug-av-llc.dutycycle", "Duty cycle", base.DEC)
local f_voltage = ProtoField.float("homeplug-av-llc.voltage", "Voltage", base.DEC)
local f_cpstate = ProtoField.int8("homeplug-av-llc.cpstate", "CP State")
local f_acmax = ProtoField.string("homeplug-av-llc.ac_max", "AC max current")
local f_result = ProtoField.string("homeplug-av-llc.result", "Result")

p_hpav_llc.fields = {f_freq, f_dutycycle, f_voltage, f_cpstate, f_acmax, f_result}

local function extract_infos_spidcom(buf)
local freq = buf(9, 2):le_int()
local dutycycle = buf(11, 2):le_int() / 10
local voltage = buf(13, 2):le_int() / 1000
local result = buf(8, 1):int()
return freq, dutycycle, voltage, result
end

local function extract_infos_iotecha(buf)
local freq = buf(15, 2):le_int()
local dutycycle = buf(14, 1):le_int()
local voltage = buf(17, 2):le_int() / 1000
return freq, dutycycle, voltage, -1
end

local function get_ac_max_current(dutycycle)
if dutycycle < 8 then
return -1
elseif dutycycle < 10 then
return 6
elseif dutycycle < 85 then
return dutycycle * 0.6
elseif dutycycle < 96 then
return (dutycycle - 64) * 2.5
elseif dutycycle < 97 then
return 80
else
return -1
end
end

local function get_cp_state(freq, dutycycle, voltage)
local StateA = 12
local StateB = 9
local StateC = 6
local StateD = 3
local StateEF = 0
local Tolerance = 1

if (dutycycle == 0 or dutycycle == 100) and freq > 0 then
return "-"
end

local cp_state_out
if voltage <= (StateA + Tolerance) and voltage >= (StateA - Tolerance) then
cp_state_out = "A"
elseif voltage <= (StateB + Tolerance) and voltage >= (StateB - Tolerance) then
cp_state_out = "B"
elseif voltage <= (StateC + Tolerance) and voltage >= (StateC - Tolerance) then
cp_state_out = "C"
elseif voltage <= (StateD + Tolerance) and voltage >= (StateD - Tolerance) then
cp_state_out = "D"
elseif voltage <= (StateEF + Tolerance) then
return "E/F"
else
return "-"
end

local pwm_active = dutycycle > 0 and dutycycle < 100 and freq > 950 and freq < 1050
if pwm_active then
return cp_state_out .. "2"
else
return cp_state_out .. "1"
end
end

local function cp_state_to_int(cp_state)
--- used for I/O Graph plotting of CP State
if cp_state == "A1" then
return 1
elseif cp_state == "A2" then
return 2
elseif cp_state == "B1" then
return 3
elseif cp_state == "B2" then
return 4
elseif cp_state == "C1" then
return 5
elseif cp_state == "C2" then
return 6
elseif cp_state == "D1" then
return 7
elseif cp_state == "D2" then
return 8
elseif cp_state == "E/F" then
return -1
end
return 0
end

function p_hpav_llc.dissector(buf, pinfo, root)
if buf:len() == 0 then
return 0
end

-- always call default homeplug-av dissector first
local hpav_dissector = Dissector.get("homeplug-av")
local consumed_bytes_hpav
if hpav_dissector ~= nil then
consumed_bytes_hpav = hpav_dissector:call(buf, pinfo, root)
else
-- some older wireshark versions does not have a homeplug-av dissector
consumed_bytes_hpav = Dissector.get("data"):call(buf, pinfo, root)
pinfo.cols.info = "Homeplug AV"
end

local mac_mme_type = buf(1, 2):le_uint()
local mme_vendor = buf(5, 3):uint()
local freq, dutycycle, voltage, result
if mme_vendor == 0x0013D7 and mac_mme_type == 0xA10E then -- Vendor OUI: SPIDCOM Technologies SA
freq, dutycycle, voltage, result = extract_infos_spidcom(buf)
elseif mme_vendor == 0x0080E1 and mac_mme_type == 0xA22E then -- Vendor OUI: ST/IoTecha
freq, dutycycle, voltage, result = extract_infos_iotecha(buf)
else
-- default homeplug-av packet
return consumed_bytes_hpav
end

pinfo.cols.protocol = "HomePlug AV LLC"
local subtree = root:add(p_hpav_llc, buf(0))

local elem_frequency = subtree:add(f_freq, freq)
elem_frequency:append_text("Hz")
-- frequency bitmask: 001
if result == 1 or result == 3 or result == 5 or result == 7 then
TGruett marked this conversation as resolved.
Show resolved Hide resolved
elem_frequency:append_text(" (changed)")
end

local elem_dutycycle = subtree:add(f_dutycycle, dutycycle)
elem_dutycycle:append_text("%")
-- duty cycle bitmask: 010
if result == 2 or result == 3 or result == 6 or result == 7 then
elem_dutycycle:append_text(" (changed)")
end

local elem_voltage = subtree:add(f_voltage, voltage)
elem_voltage:append_text("V")
-- voltage bitmask: 100
if result == 4 or result == 5 or result == 6 or result == 7 then
elem_voltage:append_text(" (changed)")
end

local cp_state = get_cp_state(freq, dutycycle, voltage)
subtree:add(f_cpstate, cp_state_to_int(cp_state)):set_text("CP State: " .. cp_state) -- store as int to allow plotting

local ac_max_current = get_ac_max_current(dutycycle)
if ac_max_current ~= -1 then
subtree:add(f_acmax, ac_max_current):append_text("A")
pinfo.cols.info = "CP State: " .. cp_state .. " [AC max current: " .. ac_max_current .. "A]"
else
pinfo.cols.info = "CP State: " .. cp_state
end
return buf:len()
end

DissectorTable.get("ethertype"):add(0x88e1, p_hpav_llc)
Loading