2 * Routines for dissecting the ATA over Ethernet protocol.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <epan/packet.h>
27 #include <epan/wmem/wmem.h>
28 #include <epan/conversation.h>
31 void proto_register_aoe(void);
32 void proto_reg_handoff_aoe(void);
35 static int hf_aoe_version
=-1;
36 static int hf_aoe_flags_response
=-1;
37 static int hf_aoe_flags_error
=-1;
38 static int hf_aoe_error
=-1;
39 static int hf_aoe_major
=-1;
40 static int hf_aoe_minor
=-1;
41 static int hf_aoe_cmd
=-1;
42 static int hf_aoe_tag
=-1;
43 static int hf_aoe_aflags_e
=-1;
44 static int hf_aoe_aflags_d
=-1;
45 static int hf_aoe_aflags_a
=-1;
46 static int hf_aoe_aflags_w
=-1;
47 static int hf_aoe_err_feature
=-1;
48 static int hf_aoe_sector_count
=-1;
49 static int hf_aoe_acmd
=-1;
50 static int hf_aoe_astatus
=-1;
51 static int hf_aoe_lba
=-1;
52 static int hf_aoe_response_in
=-1;
53 static int hf_aoe_response_to
=-1;
54 static int hf_aoe_time
=-1;
56 static gint ett_aoe
= -1;
57 static gint ett_aoe_flags
= -1;
59 #define AOE_FLAGS_RESPONSE 0x08
60 #define AOE_FLAGS_ERROR 0x04
62 #define AOE_AFLAGS_E 0x40
63 #define AOE_AFLAGS_D 0x10
64 #define AOE_AFLAGS_A 0x02
65 #define AOE_AFLAGS_W 0x01
67 static const true_false_string tfs_aflags_e
= {
68 "LBA48 extended command",
71 static const true_false_string tfs_aflags_d
= {
75 static const true_false_string tfs_aflags_a
= {
79 static const true_false_string tfs_aflags_w
= {
80 "WRITE to the device",
84 static const true_false_string tfs_response
= {
89 static const true_false_string tfs_error
= {
94 static const value_string error_vals
[] = {
95 { 1, "Unrecognized command code" },
96 { 2, "Bad argument parameter" },
97 { 3, "Device unavailable" },
98 { 4, "Config string present" },
99 { 5, "Unsupported version" },
103 #define AOE_CMD_ISSUE_ATA_COMMAND 0
104 #define AOE_CMD_QUERY_CONFIG_INFO 1
105 static const value_string cmd_vals
[] = {
106 { AOE_CMD_ISSUE_ATA_COMMAND
, "Issue ATA Command" },
107 { AOE_CMD_QUERY_CONFIG_INFO
, "Query Config Information" },
111 static const value_string ata_cmd_vals
[] = {
113 { 0x08, "Atapi soft reset" },
114 { 0x10, "Recalibrate" },
115 { 0x20, "Read sectors (with retry)" },
116 { 0x21, "Read sectors (no retry)" },
117 { 0x22, "Read long (with retry)" },
118 { 0x23, "Read long (no retry)" },
119 { 0x24, "Read ext" },
120 { 0x30, "Write sectors (with retry)" },
121 { 0x31, "Write sectors (no retry)" },
122 { 0x32, "Write long (with retry)" },
123 { 0x33, "Write long (no retry)" },
124 { 0x34, "Write ext" },
125 { 0x3c, "Write verify" },
126 { 0x40, "Read verify sectors (with retry)" },
127 { 0x41, "Read verify sectors (no retry)" },
128 { 0x50, "Format track" },
130 { 0x90, "Execute device diagnostics" },
131 { 0x91, "Initialize device parameters" },
132 { 0x92, "Download microcode" },
133 { 0x94, "Standy immediate" },
134 { 0x95, "Idle immediate" },
137 { 0x98, "Check power mode" },
139 { 0xa0, "Atapi packet" },
140 { 0xa1, "Atapi identify device" },
141 { 0xa2, "Atapi service" },
143 { 0xc4, "Read multiple" },
144 { 0xc5, "Write multiple" },
145 { 0xc6, "Set multiple mode" },
146 { 0xc8, "Read dma (with retry)" },
147 { 0xc9, "Read dma (no retry)" },
148 { 0xca, "Write dma (with retry)" },
149 { 0xcb, "Write dma (no retry)" },
150 { 0xde, "Door lock" },
151 { 0xdf, "Door unlock" },
152 { 0xe0, "Standy immediate" },
153 { 0xe1, "Idle immediate" },
156 { 0xe4, "Read buffer" },
157 { 0xe5, "Check power mode" },
159 { 0xe8, "Write buffer" },
160 { 0xec, "Identify Device" },
161 { 0xed, "Media eject" },
162 { 0xee, "Identify device dma" },
163 { 0xef, "Set features" },
164 { 0xf1, "Security set password" },
165 { 0xf2, "Security unlock" },
166 { 0xf3, "Security erase prepare" },
167 { 0xf4, "Security erase unit" },
168 { 0xf5, "Security freeze" },
169 { 0xf6, "Security disable password" },
173 typedef struct ata_info_t
{
175 void *conversation
; /* just used to multiplex different conversations */
176 guint32 request_frame
;
177 guint32 response_frame
;
181 static GHashTable
*ata_cmd_unmatched
= NULL
;
182 static GHashTable
*ata_cmd_matched
= NULL
;
185 ata_cmd_hash_matched(gconstpointer k
)
187 return GPOINTER_TO_UINT(k
);
191 ata_cmd_equal_matched(gconstpointer k1
, gconstpointer k2
)
197 ata_cmd_hash_unmatched(gconstpointer k
)
199 const ata_info_t
*key
= (const ata_info_t
*)k
;
205 ata_cmd_equal_unmatched(gconstpointer k1
, gconstpointer k2
)
207 const ata_info_t
*key1
= (const ata_info_t
*)k1
;
208 const ata_info_t
*key2
= (const ata_info_t
*)k2
;
210 return (key1
->tag
==key2
->tag
)&&(key1
->conversation
==key2
->conversation
);
214 dissect_ata_pdu(packet_info
*pinfo
, proto_tree
*tree
, tvbuff_t
*tvb
, int offset
, gboolean response
, guint32 tag
)
216 proto_item
*tmp_item
;
219 ata_info_t
*ata_info
=NULL
;
220 conversation_t
*conversation
;
222 /* only create a conversation for ATA commands */
223 conversation
= find_or_create_conversation(pinfo
);
225 if( !(pinfo
->fd
->flags
.visited
) ){
227 ata_info_t
*tmp_ata_info
;
228 /* first time we see this request so add a struct for request/response
230 ata_info
=wmem_new(wmem_file_scope(), ata_info_t
);
232 ata_info
->conversation
=conversation
;
233 ata_info
->request_frame
=pinfo
->fd
->num
;
234 ata_info
->response_frame
=0;
235 ata_info
->cmd
=tvb_get_guint8(tvb
, offset
+3);
236 ata_info
->req_time
=pinfo
->fd
->abs_ts
;
238 tmp_ata_info
=(ata_info_t
*)g_hash_table_lookup(ata_cmd_unmatched
, ata_info
);
240 g_hash_table_remove(ata_cmd_unmatched
, tmp_ata_info
);
242 g_hash_table_insert(ata_cmd_unmatched
, ata_info
, ata_info
);
244 ata_info_t tmp_ata_info
;
245 /* first time we see this response so see if we can match it with
247 tmp_ata_info
.tag
=tag
;
248 tmp_ata_info
.conversation
=conversation
;
249 ata_info
=(ata_info_t
*)g_hash_table_lookup(ata_cmd_unmatched
, &tmp_ata_info
);
250 /* woo hoo we could, so no need to store this in unmatched any more,
251 move both request and response to the matched table */
253 ata_info
->response_frame
=pinfo
->fd
->num
;
254 g_hash_table_remove(ata_cmd_unmatched
, ata_info
);
255 g_hash_table_insert(ata_cmd_matched
, GUINT_TO_POINTER(ata_info
->request_frame
), ata_info
);
256 g_hash_table_insert(ata_cmd_matched
, GUINT_TO_POINTER(ata_info
->response_frame
), ata_info
);
260 ata_info
=(ata_info_t
*)g_hash_table_lookup(ata_cmd_matched
, GUINT_TO_POINTER(pinfo
->fd
->num
));
265 if(ata_info
->request_frame
){
267 tmp_item
=proto_tree_add_uint(tree
, hf_aoe_response_to
, tvb
, 0, 0, ata_info
->request_frame
);
268 PROTO_ITEM_SET_GENERATED(tmp_item
);
269 nstime_delta(&delta_ts
, &pinfo
->fd
->abs_ts
, &ata_info
->req_time
);
270 tmp_item
=proto_tree_add_time(tree
, hf_aoe_time
, tvb
, offset
, 0, &delta_ts
);
271 PROTO_ITEM_SET_GENERATED(tmp_item
);
274 if(ata_info
->response_frame
){
275 tmp_item
=proto_tree_add_uint(tree
, hf_aoe_response_in
, tvb
, 0, 0, ata_info
->response_frame
);
276 PROTO_ITEM_SET_GENERATED(tmp_item
);
282 aflags
=tvb_get_guint8(tvb
, offset
);
283 proto_tree_add_item(tree
, hf_aoe_aflags_e
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
284 if(aflags
&AOE_AFLAGS_E
){
285 proto_tree_add_item(tree
, hf_aoe_aflags_d
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
287 if(aflags
&AOE_AFLAGS_W
){
288 proto_tree_add_item(tree
, hf_aoe_aflags_a
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
290 proto_tree_add_item(tree
, hf_aoe_aflags_w
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
294 proto_tree_add_item(tree
, hf_aoe_err_feature
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
298 proto_tree_add_item(tree
, hf_aoe_sector_count
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
301 /* ata command/status */
303 proto_tree_add_item(tree
, hf_aoe_acmd
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
304 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " ATA:%s", val_to_str(tvb_get_guint8(tvb
, offset
), ata_cmd_vals
, " Unknown ATA<0x%02x>"));
306 proto_tree_add_item(tree
, hf_aoe_astatus
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
307 if(ata_info
!= NULL
&& ata_info
->request_frame
){
308 /* we dont know what command it was unless we saw the request_frame */
309 tmp_item
=proto_tree_add_uint(tree
, hf_aoe_acmd
, tvb
, 0, 0, ata_info
->cmd
);
310 PROTO_ITEM_SET_GENERATED(tmp_item
);
311 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " ATA:%s", val_to_str(ata_info
->cmd
, ata_cmd_vals
, " Unknown ATA<0x%02x>"));
316 /*lba probably complete wrong */
317 lba
=tvb_get_letohs(tvb
, offset
+4);
318 lba
=(lba
<<32)|tvb_get_letohl(tvb
, offset
);
320 proto_tree_add_uint64(tree
, hf_aoe_lba
, tvb
, offset
-8, 6, lba
);
325 dissect_aoe_v1(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
329 proto_item
*flags_item
=NULL
;
330 proto_tree
*flags_tree
=NULL
;
332 /* read and dissect the flags */
333 flags
=tvb_get_guint8(tvb
, 0)&0x0f;
335 flags_item
=proto_tree_add_text(tree
, tvb
, 0, 1, "Flags:");
336 flags_tree
=proto_item_add_subtree(flags_item
, ett_aoe_flags
);
338 proto_tree_add_item(flags_tree
, hf_aoe_flags_response
, tvb
, 0, 1, ENC_BIG_ENDIAN
);
339 proto_tree_add_item(flags_tree
, hf_aoe_flags_error
, tvb
, 0, 1, ENC_BIG_ENDIAN
);
341 proto_item_append_text(flags_item
,(flags
&AOE_FLAGS_RESPONSE
)?" Response":" Request");
342 if(flags
&AOE_FLAGS_ERROR
){
343 proto_item_append_text(flags_item
, " Error");
349 if(flags
&AOE_FLAGS_ERROR
){
350 proto_tree_add_item(tree
, hf_aoe_error
, tvb
, 1, 1, ENC_BIG_ENDIAN
);
351 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Error:%s ", val_to_str(tvb_get_guint8(tvb
, 1), error_vals
, "Unknown error<%d>"));
354 /* major/minor address */
355 proto_tree_add_item(tree
, hf_aoe_major
, tvb
, 2, 2, ENC_BIG_ENDIAN
);
356 proto_tree_add_item(tree
, hf_aoe_minor
, tvb
, 4, 1, ENC_BIG_ENDIAN
);
359 cmd
=tvb_get_guint8(tvb
, 5);
360 proto_tree_add_item(tree
, hf_aoe_cmd
, tvb
, 5, 1, ENC_BIG_ENDIAN
);
361 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%s %s", val_to_str(cmd
, cmd_vals
, "Unknown command<%d>"), (flags
&AOE_FLAGS_RESPONSE
)?"Response":"Request");
365 tag
=tvb_get_letohl(tvb
, 6);
366 proto_tree_add_item(tree
, hf_aoe_tag
, tvb
, 6, 4, ENC_BIG_ENDIAN
);
370 case AOE_CMD_ISSUE_ATA_COMMAND
:
371 dissect_ata_pdu(pinfo
, tree
, tvb
, 10, flags
&AOE_FLAGS_RESPONSE
, tag
);
373 case AOE_CMD_QUERY_CONFIG_INFO
:
380 dissect_aoe(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
)
382 proto_item
*item
=NULL
;
383 proto_tree
*tree
=NULL
;
386 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "AoE");
387 col_clear(pinfo
->cinfo
, COL_INFO
);
390 item
= proto_tree_add_item(parent_tree
, proto_aoe
, tvb
, 0, -1, ENC_NA
);
391 tree
= proto_item_add_subtree(item
, ett_aoe
);
394 version
=tvb_get_guint8(tvb
, 0)>>4;
395 proto_tree_add_uint(tree
, hf_aoe_version
, tvb
, 0, 1, version
);
398 dissect_aoe_v1(tvb
, pinfo
, tree
);
406 if(ata_cmd_unmatched
){
407 g_hash_table_destroy(ata_cmd_unmatched
);
408 ata_cmd_unmatched
=NULL
;
410 ata_cmd_unmatched
=g_hash_table_new(ata_cmd_hash_unmatched
, ata_cmd_equal_unmatched
);
413 g_hash_table_destroy(ata_cmd_matched
);
414 ata_cmd_matched
=NULL
;
416 ata_cmd_matched
=g_hash_table_new(ata_cmd_hash_matched
, ata_cmd_equal_matched
);
421 proto_register_aoe(void)
424 static hf_register_info hf
[] = {
426 { "Command", "aoe.cmd", FT_UINT8
, BASE_DEC
, VALS(cmd_vals
), 0x0,
427 "AOE Command", HFILL
}},
429 { "Version", "aoe.version", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
430 "Version of the AOE protocol", HFILL
}},
432 { "Error", "aoe.error", FT_UINT8
, BASE_DEC
, VALS(error_vals
), 0x0,
433 "Error code", HFILL
}},
434 { &hf_aoe_err_feature
,
435 { "Err/Feature", "aoe.err_feature", FT_UINT8
, BASE_HEX
, NULL
, 0x0,
437 { &hf_aoe_sector_count
,
438 { "Sector Count", "aoe.sector_count", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
440 { &hf_aoe_flags_response
,
441 { "Response flag", "aoe.response", FT_BOOLEAN
, 8, TFS(&tfs_response
), AOE_FLAGS_RESPONSE
, "Whether this is a response PDU or not", HFILL
}},
442 { &hf_aoe_flags_error
,
443 { "Error flag", "aoe.flags_error", FT_BOOLEAN
, 8, TFS(&tfs_error
), AOE_FLAGS_ERROR
, "Whether this is an error PDU or not", HFILL
}},
445 { "Major", "aoe.major", FT_UINT16
, BASE_HEX
, NULL
, 0x0,
446 "Major address", HFILL
}},
448 { "Minor", "aoe.minor", FT_UINT8
, BASE_HEX
, NULL
, 0x0,
449 "Minor address", HFILL
}},
451 { "ATA Cmd", "aoe.ata.cmd", FT_UINT8
, BASE_HEX
, VALS(ata_cmd_vals
), 0x0,
452 "ATA command opcode", HFILL
}},
454 { "ATA Status", "aoe.ata.status", FT_UINT8
, BASE_HEX
, NULL
, 0x0,
455 "ATA status bits", HFILL
}},
457 { "Tag", "aoe.tag", FT_UINT32
, BASE_HEX
, NULL
, 0x0,
458 "Command Tag", HFILL
}},
460 { "E", "aoe.aflags.e", FT_BOOLEAN
, 8, TFS(&tfs_aflags_e
), AOE_AFLAGS_E
, "Whether this is a normal or LBA48 command", HFILL
}},
462 { "D", "aoe.aflags.d", FT_BOOLEAN
, 8, TFS(&tfs_aflags_d
), AOE_AFLAGS_D
, NULL
, HFILL
}},
464 { "A", "aoe.aflags.a", FT_BOOLEAN
, 8, TFS(&tfs_aflags_a
), AOE_AFLAGS_A
, "Whether this is an asynchronous write or not", HFILL
}},
466 { "W", "aoe.aflags.w", FT_BOOLEAN
, 8, TFS(&tfs_aflags_w
), AOE_AFLAGS_W
, "Is this a command writing data to the device or not", HFILL
}},
468 { "Lba", "aoe.lba", FT_UINT64
, BASE_HEX
, NULL
, 0x00, "Lba address", HFILL
}},
469 { &hf_aoe_response_in
,
470 { "Response In", "aoe.response_in", FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0, "The response to this packet is in this frame", HFILL
}},
471 { &hf_aoe_response_to
,
472 { "Response To", "aoe.response_to", FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0, "This is a response to the ATA command in this frame", HFILL
}},
474 { "Time from request", "aoe.time", FT_RELATIVE_TIME
, BASE_NONE
, NULL
, 0, "Time between Request and Reply for ATA calls", HFILL
}},
477 static gint
*ett
[] = {
482 proto_aoe
= proto_register_protocol("ATAoverEthernet", "AOE", "aoe");
483 proto_register_field_array(proto_aoe
, hf
, array_length(hf
));
484 proto_register_subtree_array(ett
, array_length(ett
));
486 register_dissector("aoe", dissect_aoe
, proto_aoe
);
487 register_init_routine(ata_init
);
491 proto_reg_handoff_aoe(void)
493 dissector_handle_t aoe_handle
;
495 aoe_handle
= find_dissector("aoe");
496 dissector_add_uint("ethertype", ETHERTYPE_AOE
, aoe_handle
);