pythonでDoIP ~⑤DoIP通信プロトコル実装~
ついに最終回としてDoIP通信プロトコルを実装していきます。
設計しているうちにいろいろ見直ししてきたので、以前検討した以下GUIから若干変わっています。
Contents
通信プロトコル対応内容
以下のDoIP通信を実現できるような実装をしていきます。
コマンド | 対応 | プロトコル | 実装 | 共通 | type | len | peyload |
---|---|---|---|---|---|---|---|
Generic DoIP header negative acknowledge | 非対応 | UDP/ TCP | サーバ/ クライアント | 02FD | 0000 | 00000001 | NACKコード (1) |
Vehicle identification request message | 対応 | UDP | クライアント | 02FD | 0001 | 00000000 | – |
Vehicle identification request message with EID | 対応 | UDP | クライアント | 02FD | 0002 | 00000006 | EID (6) *任意 |
Vehicle identification request message with VIN | 対応 | UDP | クライアント | 02FD | 0003 | 00000011 | VIN (17) *任意 |
vehicle identification response message | 対応 | UDP | サーバ | 02FD | 0004 | 00000021 | VIN (17) *任意 論理アドレス (2) *任意 EID (6) *任意 GID (6) *任意 アクションコード (1) *0x00 固定 VIN / GID同期状態 (1) *0x00 固定 |
Vehicle announcement message | 非対応 | UDP | サーバ | 02FD | 0004 | 00000021 | VIN (17) *任意 論理アドレス (2) *任意 EID (6) *任意 GID (6) *0x000000000001固定 アクションコード (1) *0x00 固定 VIN / GID同期状態 (1) *0x00 固定 |
DoIP entity status request | 対応 | UDP | クライアント | 02FD | 4001 | 00000000 | – |
DoIP entity status response | 対応 | UDP | サーバ | 02FD | 4002 | 00000007 | ノードタイプ (1) *0x01固定 同時接続可能ソケット数 (1) *任意 同時接続中のソケット数 (1) *任意 扱える最大データ長 (4) *任意 |
Diagnostic power mode information request | 対応 | UDP | クライアント | 02FD | 4003 | 00000000 | – |
Diagnostic power mode information response | 対応 | UDP | サーバ | 02FD | 4004 | 00000001 | 診断状態 (1) *任意 |
Routing activation request | 対応 | TCP | クライアント | 02FD | 0005 | 00000007 | SA (2) *任意 タイプ (1) *0x00 固定 リザーブ (4) *0x00000000 固定 |
Routing activation response | 対応 | TCP | サーバ | 02FD | 0006 | 00000009 | SA (2) *任意 TA (2) *任意 応答コード (1) *0x10固定 リザーブ (4) *0x00000000 固定 |
Alive check request | 対応 | TCP | クライアント | 02FD | 0007 | 00000000 | – |
Alive check response | 対応 | TCP | サーバ | 02FD | 0008 | 00000002 | SA (2) *任意 |
Diagnostic message | 対応 | TCP | サーバ/ クライアント | 02FD | 8001 | 任意 | SA (2) *任意 TA (2) *任意 ユーザデータ *任意 |
Diagnostic message positive acknowledgement | 対応 | TCP | サーバ/ クライアント | 02FD | 8002 | 00000005 | SA (2) *任意 TA (2) *任意 ACKコード (1) *0x00固定 |
Diagnostic message negative acknowledgement | 非対応 | TCP | サーバ/ クライアント | 02FD | 8003 | 00000005 | SA (2) *任意 TA (2) *任意 NACKコード (1) *0x00固定 |
peyload列の任意パラメータについては、GUIから任意の値を設定できるようにします。
また、異常系については今回の実装対象外とします。
各コマンドデータの詳細については以下過去記事を確認してください。
ソース
メイン処理からの要求を受けてDoIPメッセージの作成、受信データの解析を行うようなコードを作成しました。
やっつけなので見直すべき箇所が多々あり申し訳ない。
全コードについては以下参照願います。
DoIPクライアント
import pandas as pd
# 送信コマンドテーブル
doip_cmd_tbl_send = \
[ #"cmand" ,"protocol","KEY" ,"CMN" ,"TYPE","LEN" ,"VALUE1" ,"VALUE2" ,"VALUE3"
["Vehicle identification request message" ,"UDP" ,"-CMD_UDP_VI_REQ-" ,"02FD","0001","00000000",None ,None ,None ],
["Vehicle identification request message with EID" ,"UDP" ,"-CMD_UDP_VI_REQ_EID-" ,"02FD","0002","00000006","-TXT_EID-" ,None ,None ],
["Vehicle identification request message with VIN" ,"UDP" ,"-CMD_UDP_VI_REQ_VIN-" ,"02FD","0003","00000011","-TXT_VIN-" ,None ,None ],
["DoIP entity status request" ,"UDP" ,"-CMD_UDP_DES_REQ-" ,"02FD","4001","00000000",None ,None ,None ],
["Diagnostic power mode information request" ,"UDP" ,"-CMD_UDP_DPMI_REQ-" ,"02FD","4003","00000000",None ,None ,None ],
["Routing activation request" ,"TCP" ,"-CMD_TCP_RA_REQ-" ,"02FD","0005","00000007","-TXT_SA1-" ,None ,None ],
["Alive check request" ,"TCP" ,"-CMD_TCP_AC_REQ-" ,"02FD","0007","00000000",None ,None ,None ],
["Diagnostic message" ,"TCP" ,"-CMD_TCP_DM-" ,"02FD","8001","00000000","-TXT_SA2-" ,"-TXT_TA-" ,"-TXT_DIAG-" ],
#["FREE MESSAGE" ,"UDP" ,"-CMD_UDP_FREE-" ,None ,None ,None ,"-TXT_UDP_FREE-" ,None ,None ],
#["FREE MESSAGE" ,"TCP" ,"-CMD_TCP_FREE-" ,None ,None ,None ,"-TXT_TCP_FREE-" ,None ,None ],
#["Diagnostic message positive acknowledgement" ,"-CMD_TCP_DMA-" ,"02FD","8002","00000005","-TXT_SA2-" ,"-TXT_TA-" ,None ],
]
# 受信コマンドテーブル
doip_cmd_tbl_recv = \
[
["vehicle identification response message" ,"UDP" ,"-CMD_UDP_VI_RES-" ,"02FD","0004","00000021"],
["DoIP entity status response" ,"UDP" ,"-CMD_UDP_DES_RES-" ,"02FD","4002","00000007"],
["Diagnostic power mode information response" ,"UDP" ,"-CMD_UDP_DPMI_RES-" ,"02FD","4004","00000001"],
["Routing activation response" ,"TCP" ,"-CMD_TCP_RA_RES-" ,"02FD","0006","00000009"],
["Alive check response" ,"TCP" ,"-CMD_TCP_AC_RES-" ,"02FD","0008","00000002"],
["Diagnostic message" ,"TCP" ,"-CMD_TCP_DM-" ,"02FD","8001","00000000"],
["Diagnostic message positive acknowledgement" ,"TCP" ,"-CMD_TCP_DMA-" ,"02FD","8002","00000005"],
]
doip_send_df = pd.DataFrame((doip_cmd_tbl_send), columns=["cmand","protocol", "KEY", "CMN", "TYPE","LEN","VALUE1","VALUE2","VALUE3"])
doip_recv_df = pd.DataFrame((doip_cmd_tbl_recv), columns=["cmand","protocol", "KEY", "CMN", "TYPE","LEN"])
# 送信データ作成関数
def doip_make_msg(values,protocol):
send_msg = None
send_data = None
# 送信コマンドテーブル検索
for index, row in doip_send_df.iterrows():
# 送信データ種別を判定
if values[row["KEY"]] == True and protocol == row["protocol"] :
send_msg = row["cmand"]
payload_data = ""
payload_len =""
# ペイロード部分作成
if row["cmand"] == "Routing activation request":
payload_data += values[row["VALUE1"]]
payload_data += "0000000000"
else:
if row["VALUE1"] != None:
payload_data += values[row["VALUE1"]]
if row["VALUE2"] != None:
payload_data += values[row["VALUE2"]]
if row["VALUE3"] != None:
payload_data += values[row["VALUE3"]]
payload_len = hex(int(len(payload_data)/2))[2:]
payload_len = payload_len.zfill(8)
if None != payload_data:
send_data = row["CMN"] + row["TYPE"] + payload_len + payload_data
break
return send_msg ,send_data
# ACK送信データ作成関数
def doip_make_msg_ack(values):
send_msg = "02FD800200000005" + values["-TXT_SA2-"] + "00"
return send_msg
# 受信データ判定関数
def doip_recv_msg(data,protocol):
recv_msg = ""
type = data[4:8]
for index, row in doip_recv_df.iterrows():
# 受信データ種別を判定
if type == row["TYPE"] and protocol == row["protocol"] :
recv_msg = row["cmand"]
return recv_msg
DoIPサーバ
import pandas as pd
import app
# 初期設定
g_values = ""
g_sa = ""
# 応答コマンドテーブル
doip_cmd_tbl_send = \
[ #"REQUEST" ,"REQTYPE" ,"RESPONCE" ,"PROTCOL","CMN" ,"TYPE","LEN" ,"VALUE1" ,"VALUE2" ,"VALUE3" ,"VALUE4"
["Vehicle identification request message" ,"0001" ,"vehicle identification response message" ,"UDP" ,"02FD","0004","00000021","-VIN_GW-" ,"-LOGI_ADDR-","-EID_GW-" ,"-GID_GW-" ],
["Vehicle identification request message with EID","0002" ,"vehicle identification response message" ,"UDP" ,"02FD","0004","00000021","-VIN_GW-" ,"-LOGI_ADDR-","-EID_GW-" ,"-GID_GW-" ],
["Vehicle identification request message with VIN","0003" ,"vehicle identification response message" ,"UDP" ,"02FD","0004","00000021","-VIN_GW-" ,"-LOGI_ADDR-","-EID_GW-" ,"-GID_GW-" ],
["DoIP entity status request" ,"4001" ,"DoIP entity status response" ,"UDP" ,"02FD","4002","00000007","-NODE_TYPE-","-CONN_NUM-" ,"-CONN_NOW_NUM-" ,"-MAX_LEN-" ],
["Diagnostic power mode information request" ,"4003" ,"Diagnostic power mode information response" ,"UDP" ,"02FD","4004","00000001","-DIAG_ST-" ,None ,None ,None ],
["Routing activation request" ,"0005" ,"Routing activation response" ,"TCP" ,"02FD","0006","00000009","-LOGI_ADDR-","-LOGI_ADDR-",None ,None ],
["Alive check request" ,"0007" ,"Alive check response" ,"TCP" ,"02FD","0008","00000002","-LOGI_ADDR-","-LOGI_ADDR-",None ,None ],
["Diagnostic message" ,"8001" ,"Diagnostic message positive acknowledgement","TCP" ,"02FD","8002","00000005","-LOGI_ADDR-","-LOGI_ADDR-",None ,None ],
["Diagnostic message positive acknowledgement" ,"8002" ,None ,"TCP" ,None ,None ,None ,None ,None ,None ,None ],]
doip_recv_df = pd.DataFrame((doip_cmd_tbl_send), columns=["REQ","REQTYPE","RES","PROTCOL","CMN","TYPE","LEN","VALUE1","VALUE2","VALUE3","VALUE4"])
# 初期化関数
def doip_init(values):
global g_values
g_values = values
return
# 送信データ作成関数
def doip_make_msg(row,data):
global g_sa
send_data = None
send_msg = row["RES"]
payload_data = ""
# ペイロード部分作成
if row["PROTCOL"] == "UDP":
if row["VALUE1"] != None:
payload_data += g_values[row["VALUE1"]]
if row["VALUE2"] != None:
payload_data += g_values[row["VALUE2"]]
if row["VALUE3"] != None:
payload_data += g_values[row["VALUE3"]]
if row["VALUE4"] != None:
payload_data += g_values[row["VALUE4"]]
if row["RES"] == "vehicle identification response message":
payload_data += "0000"
else:
if row["RES"] == "Routing activation response":
g_sa = data[16:20]
payload_data += g_sa + g_values[row["VALUE2"]] + "1000000000"
if row["RES"] == "Alive check response":
payload_data += g_values[row["VALUE1"]]
if row["RES"] == "Diagnostic message positive acknowledgement":
if "" != g_sa:
payload_data += g_values[row["VALUE1"]] + g_sa + "00"
# ぺーロード長作成
payload_len = hex(int(len(payload_data)/2))[2:]
payload_len = payload_len.zfill(8)
# 送信データ作成
if None != payload_data:
send_data = row["CMN"] + row["TYPE"] + payload_len + payload_data
return send_msg ,send_data
# 受信データ判定関数
def doip_recv_msg(data,protocol):
recv_msg = ""
send_msg = ""
send_data = None
type = data[4:8]
for index, row in doip_recv_df.iterrows():
# 受信データ種別を判定
if type == row["REQTYPE"] and protocol == row["PROTCOL"] :
recv_msg = row["REQ"]
if row["REQ"] != "Diagnostic message positive acknowledgement":
send_msg , send_data = doip_make_msg(row,data)
return protocol,recv_msg,data,send_msg,send_data
# ACK送信データ作成関数
def doip_make_msg_ack(values):
send_msg = "02FD800200000005" + values["-TXT_SA2-"] + "00"
return send_msg
動作確認
UIで対応しているコマンドについては一通り通信確認が取れました。
DoIPクライアントUI
DoIPサーバUI
wiresharkによるパケット確認
ローカルホスト間の通信キャプチャについては以下参照。
以前も話しましたが、wiresharkがDoIPプロトコルに対応してくれているため、通信データの確認がとてもしやすかったです。
通信フォーマットがどこか違うとwireshark上でmarformedとして教えてくれるので、デバッグがはかどりました。
最後全コマンド実施した時のキャプチャが以下。
課題
ひとまずはDoIP通信が出来たということで、これで完了とします。
今回実装できていない内容としては下記があります。
- 車両内ECUに対するルーティング処理
- タイムアウト関連
- 異常系
- ソケット管理
- UDS関連
余裕があればそのうち。