Wireshark dissector for ISO15118 EV charging Protocol

Zeung-il Kim
17 min readMay 27, 2021

--

통상 가장 하위단의 data dissector에서 분석 후 상위단으로 넘겨주는 구조. 예를 들면 Ethernet header 분석을 위해 Ethernet dissector가 동작하고 그 다음 payload를 다음 dissector로 넘기는 형태(IP dissector). ISO 15118용 dissector는 plugin 형태로 개발 되었다.

Wireshark function blocks [https://www.wireshark.org/docs/wsdg_html_chunked/ChWorksOverview.html]

wireshark_src/plugins/epan/ 아래에 이러한 custom dissector들이 모여 있다.

º Protocol Tree : Dissection information for an individual packet.
º Dissectors. The various protocol dissectors in epan/dissectors.
º Dissector Plugins - Support for implementing dissectors as
separate modules. Source code can be found in plugins.
º Display Filters - The display filter engine at epan/dfilter.

packet-iso15118.c 구조

#include "config.h"
#include <epan/packet.h> // epan : Enhanced Packet analyzer

기본적으로 들어가는 include file들 (boilerplate)

#define ISO15118_PORT 15118

처음 SDP수행하는 UDP 포트 번호 (ISO표준에서 규정되어 있음)

static int proto_iso15118 = -1;

프로토콜 핸들(prtocol handle)을 저장할 int type 변수 정의하고 -1로 초기화.

다음으로 메인 프로그램과 연결하기 위한 기본 함수 두개prtoto_register_iso15118 proto_reg_handoff_iso15118를 구성한다.

void proto_register_iso15118(void) {
static hf_register_info hf[] = {
{ &hf_iso15118_version,
{ "iso15118 Version", "iso15118.version",
FT_UINT8, BASE_HEX,
NULL, 0x0,
NULL, HFILL }
},
......
//표준에서 규정한 모든 data 정보들을 기입 해줌
}; proto_iso15118 = proto_register_protocol(
"ISO15118 Protocol", /* name */
"ISO15118", /* short name */
"iso15118" /* filter name */
);
}
// hf : header field
// ett : epan tree type
*

proto_register_iso15118 함수는 iso15118 프로토콜을 wireshark 에 등록하는데 사용된다. (proto_register_protocol 함수를 호출, 이 함수는 프로토콜 핸들은 반환한다.)

“name”과 “short name”은 wireshark 메뉴의 “Preferences” 와 “Enabled protocol” 대화창에서 보여지고, “filter name”은 “Apply a display filter” 에서 사용한다.

iso15118 통신은 초기에 UDP로 통신을 개시 한 후 TCP 연결을 맺기 때문에 두개의 dissector handle 변수를 정의 한다.

static dissector_handle_t iso15118_handle;
static dissector_handle_t dissect_iso15118_tcp_handle;

이제 프로토콜 데이터를 전달할 handoff 함수를 정의 한다.

void proto_reg_handoff_iso15118(void) {     // create dissector to do dissecting (UDP)
iso15118_handle = create_dissector_handle(dissect_iso15118_udp,
proto_iso15118);
// dissector for TCP connection
dissect_iso15118_tcp_handle =
create_dissector_handle(dissect_iso15118_tcp, proto_iso15118);
heur_dissector_add("tcp", dissect_iso15118_tcp, "ISO15118 over
TCP", "iso15118_tcp", proto_iso15118, HEURISTIC_ENABLE);
dissector_add_uint("udp.port", ISO15118_PORT, iso15118_handle);

handoff 루틴은 프로토콜 트래픽을 프로토콜 핸들러와 연결하는 역할을 하는데, 크게 두 단계로 구성되어 있다. 먼저 실제 프로토콜 분석이 이루어지는 dissector handle을 생성하고, 그 다음 이 핸들은 등록하여 해당 dissector가 호출 될 수 있도록 한다. iso15118은 UDP 통신후 (ServiceDiscovery), 실제 V2G 통신은 TCP로 이루어 지므로 udp와 tcp를 처리하는 두 개의 핸들을 생성한다.

지정된 UDP 포트로 들어오는 트래픽을 iso15118 프로토콜과 연결시켜주기 위해 dissector_add_uint 함수를 사용하고, Wireshark에서는 port번호 15118로 UDP 트래픽이 들어오면 dissect_iso15118_udp 함수를 호출하게 된다.

UDP로 연결 후 실제 V2G 통신을 위한 TCP 포트를 주고 받으면 TCP용 dissector를 호출해야 한다. 그래서 heur_dissector_add() 함수가 추가 되었다.

이제 실제 트래픽을 분석하는 dissect_iso15118_udp 함수를 작성하면 된다.

static int 
dissect_iso15118_udp(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree _U_, void *data _U_)
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "ISO15118");
col_clear(pinfo->cinfo, COL_INFO);
...... return tvb_captured_length(tvb);
}

port번호 15118로 들어오는 UDP 패킷을 분석하기 위해 dissect_iso15118_udp 함수가 호출된다. 패킷 데이터는 “tvb”라는 특수한 버퍼에 담겨져 있으며 “packet_info” 구조체에는 프로토콜에 대한 일반적인 정보가 담겨져 있어서 이 걸 보고 정보를 update할 수 있다.

“tree” 파라메터는 상세 분석이 이루어지는 부분이며 뒤에 따라 붙는 “_U_”는 컴파일러에게 파라메터들이 사용되지 않는 다는 것을 알려 줘서 warning을 발생하지 않게 한다.

“col_set_str”는 wireshark 창의 protocol 컬럼에 표시되도록 하는 것이며, “col_clear”는 Info 컬럼을 초기화 해주는 것이다.

이제 Payload에 라벨링을 해줌으로써 분석된 패킷 결과를 보기 쉽게 트리 구조로 만들어 가면 된다.

static int 
dissect_iso15118_udp(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree _U_, void *data _U_)
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "ISO15118");
col_clear(pinfo->cinfo, COL_INFO);
// 신규 서브트리를 추가 한다.
proto_item *ti = proto_tree_add_item(tree, proto_iso15118,
tvb, 0, -1, ENC_NA);
return tvb_captured_length(tvb);
}

iso15118 프로토콜 트리를 만들어 주고 다른 프로토콜을 캡슐화 하고 있지 않으므로 받은 데이터를 전부(0, -1) 사용하며, big endian 또는 little endian 엔코딩을 사용하지 않기 때문에 not applicable (ENC_NA)로 설정한다. proto_tree_add_item 함수가 호출 되면 “ISO15118” 라벨이 wireshark protocol 창에 보이게 된다. (filter 창에서 선택 가능)

이제 다음 단계로 패킷에 표시할 데이터들과 서브트리에 포함될 데이터들을 구조체로 만들어 “hf_register_info” 와 “ett” 이름으로 할당하고, (hf : header field) “proto_register_field_array()”와 “proto_register_subtree_array()” 호출하여 등록한다. 이 부분이 노가다 성이 짙다. (ett : epan tree type)

/* == Registering data structures == */ 
static int hf_iso15118_version = -1;
static int hf_iso15118_inv_version = -1;
static int hf_iso15118_payload_type = -1;
static int hf_iso15118_payload_length = -1;
static gint ett_iso15118 = -1;
static gint ett_v2gtp = -1;
static gint ett_app = -1;
static gint ett_header = -1;
static gint ett_iso15118_tls = -1;
static gint ett_cert_tree= -1;

void proto_register_iso15118(void){
static hf_register_info hf[] = {
{ &hf_iso15118_version,
{ "iso15118 Version", "iso15118.version",
FT_UINT8, BASE_HEX,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_iso15118_inv_version,
{ "iso15118 Inverse Version", "iso15118.inv_version",
FT_UINT8, BASE_HEX,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_iso15118_payload_type,
{ "iso15118 Payload Type", "iso15118.payload_type",
FT_UINT16, BASE_HEX,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_iso15118_payload_length,
{ "iso15118 Payload Length", "iso15118.payload_length",
FT_UINT32, BASE_DEC,
NULL, 0x0,
NULL, HFILL }
},
........ { &hf_iso15118_evsevoltagelimitachieved,
{ "EVSEVoltageLimitAchieved",
"iso15118.evsevoltagelimitachieved",
FT_STRING, BASE_NONE,
NULL, 0x0,
NULL, HFILL }
},
{ &hf_iso15118_evsepowerlimitachieved,
{ "EVSEPowerLimitAchieved",
"iso15118.evsepowerlimitachieved",
FT_STRING, BASE_NONE,
NULL, 0x0,
NULL, HFILL }
}
};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_iso15118,
&ett_v2gtp,
&ett_app,
&ett_header,
&ett_iso15118_tls,
&ett_cert_tree,
};
proto_iso15118 = proto_register_protocol(
"ISO15118 Protocol", /* name */
"ISO15118", /* short name */
"iso15118" /* filter name */
);
proto_register_field_array(proto_iso15118, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}############################################################
위에서 처럼 hf array 에 iso15118 표준의 모든 데이터 타입들은 정의해 놓는다.
이제 iso15118 PDU(Protocol Data Unit)들의 각 필드들을 dissect_iso15118_udp()와 dissect_iso15118_tcp() 함수에서 분석 할 수 있다. 이 함수들의 형태를 보면 아래와 같이 proto_item_add_subtree() proto_tree_add_item()함수를 호출하는 것을 볼 수 있다.
UDP 메시지인 ServiceDiscoveryRequest(SDP) Dissecting

패킷 분석을 위한 Dissector 시작

다시 “dissect_iso15118_udp” 함수로 돌아가서, 실질적으로 ISO15118 프로토콜을 분석하는 루틴을 보자. 15118 통신은 먼저 포트번호 15118을 통해 Clinet(EV)가 UDP 통신을 개시한 후 TCP 포트를 서로 주고 받은 다음 V2G TP 통신으로 넘어가게 된다.

static int 
dissect_iso15118_udp(tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree _U_, void *data _U_)
{
....
// 신규필드추가
proto_item *ti = proto_tree_add_item(tree, proto_iso15118,
tvb, 0, -1, ENC_NA);
// iso15118 프로토콜의 서브트리를 추가
proto_tree *iso15118_tree = proto_item_add_subtree(ti,
ett_iso15118);
// iso15118 아래 V2G TP(Transport Protocol) 신규 서브트리 추가
v2gtp = proto_tree_add_subtree(iso15118_tree, tvb, offset, 8,
ett_v2gtp, NULL, "V2GTP");
proto_tree_add_item(v2gtp, hf_iso15118_version, tvb, offset, 1,
ENC_BIG_ENDIAN);
offset += 1;proto_tree_add_item(v2gtp, hf_iso15118_inv_version, tvb, offset, 1,
ENC_BIG_ENDIAN);
offset += 1;......
// iso15118 아래 Application Message 신규 서브트리 추가
appli_msg = proto_tree_add_subtree(iso15118_tree, tvb, offset,
tvb_reported_length_remaining(tvb, offset), ett_app, NULL,
"Application Message");
// iso15118 udp message는 ServiceDiscoveryRequest/Response로 구성
if(type == 0x9000){ // 0x9000 SDP Request
proto_item_append_text(type_item, " (SDP request message)");
proto_tree_add_item(appli_msg, hf_iso15118_transport_protocol,
tvb, offset, 1, ENC_BIG_ENDIAN);
offset += 1;
proto_tree_add_item(appli_msg,
hf_iso15118_transport_protocol_after, tvb, offset, 1,
ENC_BIG_ENDIAN);
offset += 1;
col_set_str(pinfo->cinfo, COL_INFO, "SDP Request");

return tvb_captured_length(tvb);
}

if(type == 0x9001){ // 0x9001 SDP Response
....
}
return tvb_captured_length(tvb);
}
###############################################################
결과적으로 Header와 Application message가 아래와 같이 구성됨.
(SDP Request 메시지의 경우)
iso15118 --
|-- ett_iso15118 --
|-- ett_v2gtp --
|--version
|--inv_version
|--payload_type
|--payload_length
|-- ett_app --
|--security
|--tp_protocol
################################################################

iso15118 프로토콜용 신규 트리를 추가 하고(proto_tree_add_item), 이는 V2G TP + App.Msg 로 구성되므로, 그 아래 V2G TP 서브트리와 Application message 서브트리를 추가 한다. (proto_tree_add_subtree)

그리고 각 각 V2G TP 와 App.Msg 안의 구성요소들에 대한 item들(v2g_tp : version, inv_version, payload_type, payload_length)을 추가 한다. (proto_tree_add_item)

header field 부분을 자세히 살펴 보자. static hf_register_info hf[] = {
{ &hf_iso15118_version,
{ "iso15118 Version", "iso15118.version",
FT_UINT8, BASE_HEX,
NULL, 0x0,
NULL, HFILL }
}
...
};
/*
hf_iso15118_version : 해당 노드의 인덱스
iso15118 Version : 라벨
iso15118.version : display filter에서 사용할 수 있는 약어
(예, iso15118.version == 0x01)
FT_UINT8 : 아이템의 타입
BASE_HEX : 숫자 표시 형태 (BASE_DEC, BASE_OCT 등)
*/

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response