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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 #ifdef NEED_SNPRINTF_H
31 # include "snprintf.h"
34 #include <epan/packet.h>
35 #include <epan/conversation.h>
39 static int hf_aoe_version=-1;
40 static int hf_aoe_flags_response=-1;
41 static int hf_aoe_flags_error=-1;
42 static int hf_aoe_error=-1;
43 static int hf_aoe_major=-1;
44 static int hf_aoe_minor=-1;
45 static int hf_aoe_cmd=-1;
46 static int hf_aoe_tag=-1;
47 static int hf_aoe_aflags_e=-1;
48 static int hf_aoe_aflags_d=-1;
49 static int hf_aoe_aflags_a=-1;
50 static int hf_aoe_aflags_w=-1;
51 static int hf_aoe_err_feature=-1;
52 static int hf_aoe_sector_count=-1;
53 static int hf_aoe_acmd=-1;
54 static int hf_aoe_astatus=-1;
55 static int hf_aoe_lba=-1;
56 static int hf_aoe_response_in=-1;
57 static int hf_aoe_response_to=-1;
58 static int hf_aoe_time=-1;
60 static gint ett_aoe = -1;
61 static gint ett_aoe_flags = -1;
63 #define AOE_FLAGS_RESPONSE 0x08
64 #define AOE_FLAGS_ERROR 0x04
66 #define AOE_AFLAGS_E 0x40
67 #define AOE_AFLAGS_D 0x10
68 #define AOE_AFLAGS_A 0x02
69 #define AOE_AFLAGS_W 0x01
71 static const true_false_string tfs_aflags_e = {
72 "LBA48 extended command",
75 static const true_false_string tfs_aflags_d = {
79 static const true_false_string tfs_aflags_a = {
83 static const true_false_string tfs_aflags_w = {
84 "WRITE to the device",
88 static const true_false_string tfs_response = {
93 static const true_false_string tfs_error = {
98 static const value_string error_vals[] = {
99 { 1, "Unrecognized command code" },
100 { 2, "Bad argument parameter" },
101 { 3, "Device unavailable" },
102 { 4, "Config string present" },
103 { 5, "Unsupported version" },
107 #define AOE_CMD_ISSUE_ATA_COMMAND 0
108 #define AOE_CMD_QUERY_CONFIG_INFO 1
109 static const value_string cmd_vals[] = {
110 { AOE_CMD_ISSUE_ATA_COMMAND, "Issue ATA Command" },
111 { AOE_CMD_QUERY_CONFIG_INFO, "Query Config Information" },
115 static const value_string ata_cmd_vals[] = {
117 { 0x08, "Atapi soft reset" },
118 { 0x10, "Recalibrate" },
119 { 0x20, "Read sectors (with retry)" },
120 { 0x21, "Read sectors (no retry)" },
121 { 0x22, "Read long (with retry)" },
122 { 0x23, "Read long (no retry)" },
123 { 0x24, "Read ext" },
124 { 0x30, "Write sectors (with retry)" },
125 { 0x31, "Write sectors (no retry)" },
126 { 0x32, "Write long (with retry)" },
127 { 0x33, "Write long (no retry)" },
128 { 0x34, "Write ext" },
129 { 0x3c, "Write verify" },
130 { 0x40, "Read verify sectors (with retry)" },
131 { 0x41, "Read verify sectors (no retry)" },
132 { 0x50, "Format track" },
134 { 0x90, "Execute device diagnostics" },
135 { 0x91, "Initialize device parameters" },
136 { 0x92, "Download microcode" },
137 { 0x94, "Standy immediate" },
138 { 0x95, "Idle immediate" },
141 { 0x98, "Check power mode" },
143 { 0xa0, "Atapi packet" },
144 { 0xa1, "Atapi identify device" },
145 { 0xa2, "Atapi service" },
147 { 0xc4, "Read multiple" },
148 { 0xc5, "Write multiple" },
149 { 0xc6, "Set multiple mode" },
150 { 0xc8, "Read dma (with retry)" },
151 { 0xc9, "Read dma (no retry)" },
152 { 0xca, "Write dma (with retry)" },
153 { 0xcb, "Write dma (no retru)" },
154 { 0xde, "Door lock" },
155 { 0xdf, "Door unlock" },
156 { 0xe0, "Standy immediate" },
157 { 0xe1, "Idle immediate" },
160 { 0xe4, "Read buffer" },
161 { 0xe5, "Check power mode" },
163 { 0xe8, "Write buffer" },
164 { 0xec, "Identify Device" },
165 { 0xed, "Media eject" },
166 { 0xee, "Identify device dma" },
167 { 0xef, "Set features" },
168 { 0xf1, "Security set password" },
169 { 0xf2, "Security unlock" },
170 { 0xf3, "Security erase prepare" },
171 { 0xf4, "Security erase unit" },
172 { 0xf5, "Security freeze" },
173 { 0xf6, "Security disable password" },
177 typedef struct ata_info_t {
179 void *conversation; /* just used to multiplex different conversations */
180 guint32 request_frame;
181 guint32 response_frame;
185 static GMemChunk *ata_info_chunk = NULL;
186 static guint ata_info_chunk_count = 50;
187 static GHashTable *ata_cmd_unmatched;
188 static GHashTable *ata_cmd_matched;
191 ata_cmd_hash_matched(gconstpointer k)
193 return GPOINTER_TO_UINT(k);
197 ata_cmd_equal_matched(gconstpointer k1, gconstpointer k2)
203 ata_cmd_hash_unmatched(gconstpointer k)
205 const ata_info_t *key = k;
211 ata_cmd_equal_unmatched(gconstpointer k1, gconstpointer k2)
213 const ata_info_t *key1 = k1;
214 const ata_info_t *key2 = k2;
216 return (key1->tag==key2->tag)&&(key1->conversation==key2->conversation);
220 dissect_ata_pdu(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gboolean response, guint32 tag)
222 proto_item *tmp_item;
225 ata_info_t *ata_info=NULL;
226 conversation_t *conversation;
228 /* only create a conversation for ATA commands */
229 conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
230 pinfo->ptype, pinfo->srcport,
232 if (conversation == NULL) {
233 /* We don't yet have a conversation, so create one. */
234 conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst,
235 pinfo->ptype, pinfo->srcport,
239 if( !(pinfo->fd->flags.visited) ){
241 ata_info_t *tmp_ata_info;
242 /* first time we see this request so add a struct for request/response
244 ata_info=g_mem_chunk_alloc(ata_info_chunk);
246 ata_info->conversation=conversation;
247 ata_info->request_frame=pinfo->fd->num;
248 ata_info->response_frame=0;
249 ata_info->cmd=tvb_get_guint8(tvb, offset+3);
250 ata_info->req_time.secs=pinfo->fd->abs_secs;
251 ata_info->req_time.nsecs=pinfo->fd->abs_usecs*1000;
253 tmp_ata_info=g_hash_table_lookup(ata_cmd_unmatched, ata_info);
255 g_hash_table_remove(ata_cmd_unmatched, tmp_ata_info);
257 g_hash_table_insert(ata_cmd_unmatched, ata_info, ata_info);
259 ata_info_t tmp_ata_info;
260 /* first time we see this response so see if we can match it with
262 tmp_ata_info.tag=tag;
263 tmp_ata_info.conversation=conversation;
264 ata_info=g_hash_table_lookup(ata_cmd_unmatched, &tmp_ata_info);
265 /* woo hoo we could, so no need to store this in unmatched any more,
266 move both request and response to the matched table */
268 ata_info->response_frame=pinfo->fd->num;
269 g_hash_table_remove(ata_cmd_unmatched, ata_info);
270 g_hash_table_insert(ata_cmd_matched, GUINT_TO_POINTER(ata_info->request_frame), ata_info);
271 g_hash_table_insert(ata_cmd_matched, GUINT_TO_POINTER(ata_info->response_frame), ata_info);
275 ata_info=g_hash_table_lookup(ata_cmd_matched, GUINT_TO_POINTER(pinfo->fd->num));
280 if(ata_info->request_frame){
282 tmp_item=proto_tree_add_uint(tree, hf_aoe_response_to, tvb, 0, 0, ata_info->request_frame);
283 PROTO_ITEM_SET_GENERATED(tmp_item);
284 ns.secs= pinfo->fd->abs_secs-ata_info->req_time.secs;
285 ns.nsecs=pinfo->fd->abs_usecs*1000-ata_info->req_time.nsecs;
287 ns.nsecs+=1000000000;
290 tmp_item=proto_tree_add_time(tree, hf_aoe_time, tvb, offset, 0, &ns);
291 PROTO_ITEM_SET_GENERATED(tmp_item);
294 if(ata_info->response_frame){
295 tmp_item=proto_tree_add_uint(tree, hf_aoe_response_in, tvb, 0, 0, ata_info->response_frame);
296 PROTO_ITEM_SET_GENERATED(tmp_item);
302 aflags=tvb_get_guint8(tvb, offset);
303 proto_tree_add_item(tree, hf_aoe_aflags_e, tvb, offset, 1, FALSE);
304 if(aflags&AOE_AFLAGS_E){
305 proto_tree_add_item(tree, hf_aoe_aflags_d, tvb, offset, 1, FALSE);
307 if(aflags&AOE_AFLAGS_W){
308 proto_tree_add_item(tree, hf_aoe_aflags_a, tvb, offset, 1, FALSE);
310 proto_tree_add_item(tree, hf_aoe_aflags_w, tvb, offset, 1, FALSE);
314 proto_tree_add_item(tree, hf_aoe_err_feature, tvb, offset, 1, FALSE);
318 proto_tree_add_item(tree, hf_aoe_sector_count, tvb, offset, 1, FALSE);
321 /* ata command/status */
323 proto_tree_add_item(tree, hf_aoe_acmd, tvb, offset, 1, FALSE);
324 if (check_col(pinfo->cinfo, COL_INFO)) {
325 col_append_fstr(pinfo->cinfo, COL_INFO, " ATA:%s", val_to_str(tvb_get_guint8(tvb, offset), ata_cmd_vals, " Unknown ATA<0x%02x>"));
328 proto_tree_add_item(tree, hf_aoe_astatus, tvb, offset, 1, FALSE);
329 if(ata_info && ata_info->request_frame){
330 /* we dont know what command it was unless we saw the request_frame */
331 tmp_item=proto_tree_add_uint(tree, hf_aoe_acmd, tvb, 0, 0, ata_info->cmd);
332 PROTO_ITEM_SET_GENERATED(tmp_item);
333 if (check_col(pinfo->cinfo, COL_INFO)) {
334 col_append_fstr(pinfo->cinfo, COL_INFO, " ATA:%s", val_to_str(ata_info->cmd, ata_cmd_vals, " Unknown ATA<0x%02x>"));
340 /*lba probably complete wrong */
341 lba=tvb_get_letohs(tvb, offset+4);
342 lba=(lba<<32)|tvb_get_letohl(tvb, offset);
344 proto_tree_add_uint64(tree, hf_aoe_lba, tvb, offset-8, 6, lba);
349 dissect_aoe_v1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
353 proto_item *flags_item=NULL;
354 proto_tree *flags_tree=NULL;
356 /* read and dissect the flags */
357 flags=tvb_get_guint8(tvb, 0)&0x0f;
359 flags_item=proto_tree_add_text(tree, tvb, 0, 1, "Flags:");
360 flags_tree=proto_item_add_subtree(flags_item, ett_aoe_flags);
362 proto_tree_add_item(flags_tree, hf_aoe_flags_response, tvb, 0, 1, FALSE);
363 proto_tree_add_item(flags_tree, hf_aoe_flags_error, tvb, 0, 1, FALSE);
365 proto_item_append_text(flags_item,(flags&AOE_FLAGS_RESPONSE)?" Response":" Request");
366 if(flags&AOE_FLAGS_ERROR){
367 proto_item_append_text(flags_item, " Error");
373 if(flags&AOE_FLAGS_ERROR){
374 proto_tree_add_item(tree, hf_aoe_error, tvb, 1, 1, FALSE);
375 if (check_col(pinfo->cinfo, COL_INFO)) {
376 col_append_fstr(pinfo->cinfo, COL_INFO, "Error:%s ", val_to_str(tvb_get_guint8(tvb, 1), error_vals, "Unknown error<%d>"));
380 /* major/minor address */
381 proto_tree_add_item(tree, hf_aoe_major, tvb, 2, 2, FALSE);
382 proto_tree_add_item(tree, hf_aoe_minor, tvb, 4, 1, FALSE);
385 cmd=tvb_get_guint8(tvb, 5);
386 proto_tree_add_item(tree, hf_aoe_cmd, tvb, 5, 1, FALSE);
387 if (check_col(pinfo->cinfo, COL_INFO)) {
388 col_append_fstr(pinfo->cinfo, COL_INFO, "%s %s", val_to_str(cmd, cmd_vals, "Unknown command<%d>"), (flags&AOE_FLAGS_RESPONSE)?"Response":"Request");
392 tag=tvb_get_letohl(tvb, 6);
393 proto_tree_add_item(tree, hf_aoe_tag, tvb, 6, 4, FALSE);
397 case AOE_CMD_ISSUE_ATA_COMMAND:
398 dissect_ata_pdu(pinfo, tree, tvb, 10, flags&AOE_FLAGS_RESPONSE, tag);
400 case AOE_CMD_QUERY_CONFIG_INFO:
407 dissect_aoe(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
409 proto_item *item=NULL;
410 proto_tree *tree=NULL;
413 if (check_col(pinfo->cinfo, COL_PROTOCOL))
414 col_set_str(pinfo->cinfo, COL_PROTOCOL, "AoE");
415 if (check_col(pinfo->cinfo, COL_INFO))
416 col_clear(pinfo->cinfo, COL_INFO);
419 item = proto_tree_add_item(parent_tree, proto_aoe, tvb, 0, -1, FALSE);
420 tree = proto_item_add_subtree(item, ett_aoe);
423 version=tvb_get_guint8(tvb, 0)>>4;
424 proto_tree_add_uint(tree, hf_aoe_version, tvb, 0, 1, version);
427 dissect_aoe_v1(tvb, pinfo, tree);
435 if (ata_info_chunk != NULL){
436 g_mem_chunk_destroy(ata_info_chunk);
439 ata_info_chunk = g_mem_chunk_new("ata_info_chunk",
441 ata_info_chunk_count * sizeof(ata_info_t),
443 if(ata_cmd_unmatched){
444 g_hash_table_destroy(ata_cmd_unmatched);
445 ata_cmd_unmatched=NULL;
447 ata_cmd_unmatched=g_hash_table_new(ata_cmd_hash_unmatched, ata_cmd_equal_unmatched);
450 g_hash_table_destroy(ata_cmd_matched);
451 ata_cmd_matched=NULL;
453 ata_cmd_matched=g_hash_table_new(ata_cmd_hash_matched, ata_cmd_equal_matched);
458 proto_register_aoe(void)
461 static hf_register_info hf[] = {
463 { "Command", "aoe.cmd", FT_UINT8, BASE_DEC, VALS(cmd_vals), 0x0,
464 "AOE Command", HFILL}},
466 { "Version", "aoe.version", FT_UINT8, BASE_DEC, NULL, 0x0,
467 "Version of the AOE protocol", HFILL}},
469 { "Error", "aoe.error", FT_UINT8, BASE_DEC, VALS(error_vals), 0x0,
470 "Error code", HFILL}},
471 { &hf_aoe_err_feature,
472 { "Err/Feature", "aoe.err_feature", FT_UINT8, BASE_HEX, NULL, 0x0,
473 "Err/Feature", HFILL}},
474 { &hf_aoe_sector_count,
475 { "Sector Count", "aoe.sector_count", FT_UINT8, BASE_DEC, NULL, 0x0,
476 "Sector Count", HFILL}},
477 { &hf_aoe_flags_response,
478 { "Response flag", "aoe.response", FT_BOOLEAN, 8, TFS(&tfs_response), AOE_FLAGS_RESPONSE, "Whether this is a response PDU or not", HFILL}},
479 { &hf_aoe_flags_error,
480 { "Error flag", "aoe.error", FT_BOOLEAN, 8, TFS(&tfs_error), AOE_FLAGS_ERROR, "Whether this is an error PDU or not", HFILL}},
482 { "Major", "aoe.major", FT_UINT16, BASE_HEX, NULL, 0x0,
483 "Major address", HFILL}},
485 { "Minor", "aoe.minor", FT_UINT8, BASE_HEX, NULL, 0x0,
486 "Minor address", HFILL}},
488 { "ATA Cmd", "aoe.ata.cmd", FT_UINT8, BASE_HEX, VALS(ata_cmd_vals), 0x0,
489 "ATA command opcode", HFILL}},
491 { "ATA Status", "aoe.ata.status", FT_UINT8, BASE_HEX, NULL, 0x0,
492 "ATA status bits", HFILL}},
494 { "Tag", "aoe.tag", FT_UINT32, BASE_HEX, NULL, 0x0,
495 "Command Tag", HFILL}},
497 { "E", "aoe.aflags.e", FT_BOOLEAN, 8, TFS(&tfs_aflags_e), AOE_AFLAGS_E, "Whether this is a normal or LBA48 command", HFILL}},
499 { "D", "aoe.aflags.d", FT_BOOLEAN, 8, TFS(&tfs_aflags_d), AOE_AFLAGS_D, "", HFILL}},
501 { "A", "aoe.aflags.a", FT_BOOLEAN, 8, TFS(&tfs_aflags_a), AOE_AFLAGS_A, "Whether this is an asynchronous write or not", HFILL}},
503 { "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}},
505 { "Lba", "aoe.lba", FT_UINT64, BASE_HEX, NULL, 0x00, "Lba address", HFILL}},
506 { &hf_aoe_response_in,
507 { "Response In", "aoe.response_in", FT_FRAMENUM, BASE_DEC, NULL, 0x0, "The response to this packet is in this frame", HFILL }},
508 { &hf_aoe_response_to,
509 { "Response To", "aoe.response_to", FT_FRAMENUM, BASE_DEC, NULL, 0x0, "This is a response to the ATA command in this frame", HFILL }},
511 { "Time from request", "aoe.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0, "Time between Request and Reply for ATA calls", HFILL }},
514 static gint *ett[] = {
519 proto_aoe = proto_register_protocol("ATAoverEthernet", "AOE", "aoe");
520 proto_register_field_array(proto_aoe, hf, array_length(hf));
521 proto_register_subtree_array(ett, array_length(ett));
523 register_dissector("aoe", dissect_aoe, proto_aoe);
524 register_init_routine(ata_init);
528 proto_reg_handoff_aoe(void)
530 dissector_handle_t aoe_handle;
532 aoe_handle = find_dissector("aoe");
533 dissector_add("ethertype", ETHERTYPE_AOE, aoe_handle);