Move 3 ASN1 dissectors to 'clean' group; move 1 PIDL dissector to 'dirty' group.
[metze/wireshark/wip.git] / epan / dissectors / packet-aoe.c
1 /* packet-aoe.c
2  * Routines for dissecting the ATA over Ethernet protocol.
3  *   Ronnie Sahlberg 2004
4  *
5  * $Id$
6  *
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.
11  *
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.
16  *
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.
20  */
21
22 #include "config.h"
23
24 #include <glib.h>
25
26 #include <epan/packet.h>
27 #include <epan/emem.h>
28 #include <epan/conversation.h>
29 #include <etypes.h>
30
31 static int proto_aoe;
32 static int hf_aoe_version=-1;
33 static int hf_aoe_flags_response=-1;
34 static int hf_aoe_flags_error=-1;
35 static int hf_aoe_error=-1;
36 static int hf_aoe_major=-1;
37 static int hf_aoe_minor=-1;
38 static int hf_aoe_cmd=-1;
39 static int hf_aoe_tag=-1;
40 static int hf_aoe_aflags_e=-1;
41 static int hf_aoe_aflags_d=-1;
42 static int hf_aoe_aflags_a=-1;
43 static int hf_aoe_aflags_w=-1;
44 static int hf_aoe_err_feature=-1;
45 static int hf_aoe_sector_count=-1;
46 static int hf_aoe_acmd=-1;
47 static int hf_aoe_astatus=-1;
48 static int hf_aoe_lba=-1;
49 static int hf_aoe_response_in=-1;
50 static int hf_aoe_response_to=-1;
51 static int hf_aoe_time=-1;
52
53 static gint ett_aoe = -1;
54 static gint ett_aoe_flags = -1;
55
56 #define AOE_FLAGS_RESPONSE 0x08
57 #define AOE_FLAGS_ERROR    0x04
58
59 #define AOE_AFLAGS_E    0x40
60 #define AOE_AFLAGS_D    0x10
61 #define AOE_AFLAGS_A    0x02
62 #define AOE_AFLAGS_W    0x01
63
64 static const true_false_string tfs_aflags_e = {
65   "LBA48 extended command",
66   "Normal command"
67 };
68 static const true_false_string tfs_aflags_d = {
69   "?",
70   "?"
71 };
72 static const true_false_string tfs_aflags_a = {
73   "ASYNCHRONOUS Write",
74   "synchronous write"
75 };
76 static const true_false_string tfs_aflags_w = {
77   "WRITE to the device",
78   "No write to device"
79 };
80
81 static const true_false_string tfs_response = {
82   "Response",
83   "Request"
84 };
85
86 static const true_false_string tfs_error = {
87   "Error",
88   "No error"
89 };
90
91 static const value_string error_vals[] = {
92   { 1, "Unrecognized command code" },
93   { 2, "Bad argument parameter" },
94   { 3, "Device unavailable" },
95   { 4, "Config string present" },
96   { 5, "Unsupported version" },
97   { 0, NULL}
98 };
99
100 #define AOE_CMD_ISSUE_ATA_COMMAND  0
101 #define AOE_CMD_QUERY_CONFIG_INFO  1
102 static const value_string cmd_vals[] = {
103   { AOE_CMD_ISSUE_ATA_COMMAND, "Issue ATA Command" },
104   { AOE_CMD_QUERY_CONFIG_INFO, "Query Config Information" },
105   { 0, NULL}
106 };
107
108 static const value_string ata_cmd_vals[] = {
109   { 0x00, "NOP" },
110   { 0x08, "Atapi soft reset" },
111   { 0x10, "Recalibrate" },
112   { 0x20, "Read sectors (with retry)" },
113   { 0x21, "Read sectors (no retry)" },
114   { 0x22, "Read long (with retry)" },
115   { 0x23, "Read long (no retry)" },
116   { 0x24, "Read ext" },
117   { 0x30, "Write sectors (with retry)" },
118   { 0x31, "Write sectors (no retry)" },
119   { 0x32, "Write long (with retry)" },
120   { 0x33, "Write long (no retry)" },
121   { 0x34, "Write ext" },
122   { 0x3c, "Write verify" },
123   { 0x40, "Read verify sectors (with retry)" },
124   { 0x41, "Read verify sectors (no retry)" },
125   { 0x50, "Format track" },
126   { 0x70, "Seek" },
127   { 0x90, "Execute device diagnostics" },
128   { 0x91, "Initialize device parameters" },
129   { 0x92, "Download microcode" },
130   { 0x94, "Standy immediate" },
131   { 0x95, "Idle immediate" },
132   { 0x96, "Standby" },
133   { 0x97, "Idle" },
134   { 0x98, "Check power mode" },
135   { 0x99, "Sleep" },
136   { 0xa0, "Atapi packet" },
137   { 0xa1, "Atapi identify device" },
138   { 0xa2, "Atapi service" },
139   { 0xb0, "Smart" },
140   { 0xc4, "Read multiple" },
141   { 0xc5, "Write multiple" },
142   { 0xc6, "Set multiple mode" },
143   { 0xc8, "Read dma (with retry)" },
144   { 0xc9, "Read dma (no retry)" },
145   { 0xca, "Write dma (with retry)" },
146   { 0xcb, "Write dma (no retry)" },
147   { 0xde, "Door lock" },
148   { 0xdf, "Door unlock" },
149   { 0xe0, "Standy immediate" },
150   { 0xe1, "Idle immediate" },
151   { 0xe2, "Standby" },
152   { 0xe3, "Idle" },
153   { 0xe4, "Read buffer" },
154   { 0xe5, "Check power mode" },
155   { 0xe6, "Sleep" },
156   { 0xe8, "Write buffer" },
157   { 0xec, "Identify Device" },
158   { 0xed, "Media eject" },
159   { 0xee, "Identify device dma" },
160   { 0xef, "Set features" },
161   { 0xf1, "Security set password" },
162   { 0xf2, "Security unlock" },
163   { 0xf3, "Security erase prepare" },
164   { 0xf4, "Security erase unit" },
165   { 0xf5, "Security freeze" },
166   { 0xf6, "Security disable password" },
167   { 0, NULL}
168 };
169
170 typedef struct ata_info_t {
171   guint32 tag;
172   void *conversation; /* just used to multiplex different conversations */
173   guint32 request_frame;
174   guint32 response_frame;
175   nstime_t req_time;
176   guint8 cmd;
177 } ata_info_t;
178 static GHashTable *ata_cmd_unmatched = NULL;
179 static GHashTable *ata_cmd_matched = NULL;
180
181 static guint
182 ata_cmd_hash_matched(gconstpointer k)
183 {
184   return GPOINTER_TO_UINT(k);
185 }
186
187 static gint
188 ata_cmd_equal_matched(gconstpointer k1, gconstpointer k2)
189 {
190   return k1==k2;
191 }
192
193 static guint
194 ata_cmd_hash_unmatched(gconstpointer k)
195 {
196   const ata_info_t *key = k;
197
198   return key->tag;
199 }
200
201 static gint
202 ata_cmd_equal_unmatched(gconstpointer k1, gconstpointer k2)
203 {
204   const ata_info_t *key1 = k1;
205   const ata_info_t *key2 = k2;
206
207   return (key1->tag==key2->tag)&&(key1->conversation==key2->conversation);
208 }
209
210 static void
211 dissect_ata_pdu(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gboolean response, guint32 tag)
212 {
213   proto_item *tmp_item;
214   guint8 aflags;
215   guint64 lba;
216   ata_info_t *ata_info=NULL;
217   conversation_t *conversation;
218
219   /* only create a conversation for ATA commands */
220   conversation = find_or_create_conversation(pinfo);
221
222   if( !(pinfo->fd->flags.visited) ){
223     if(!response){
224       ata_info_t *tmp_ata_info;
225       /* first time we see this request so add a struct for request/response
226          matching */
227       ata_info=se_alloc(sizeof(ata_info_t));
228       ata_info->tag=tag;
229       ata_info->conversation=conversation;
230       ata_info->request_frame=pinfo->fd->num;
231       ata_info->response_frame=0;
232       ata_info->cmd=tvb_get_guint8(tvb, offset+3);
233       ata_info->req_time=pinfo->fd->abs_ts;
234
235       tmp_ata_info=g_hash_table_lookup(ata_cmd_unmatched, ata_info);
236       if(tmp_ata_info){
237         g_hash_table_remove(ata_cmd_unmatched, tmp_ata_info);
238       }
239       g_hash_table_insert(ata_cmd_unmatched, ata_info, ata_info);
240     } else {
241       ata_info_t tmp_ata_info;
242       /* first time we see this response so see if we can match it with
243          a request */
244       tmp_ata_info.tag=tag;
245       tmp_ata_info.conversation=conversation;
246       ata_info=g_hash_table_lookup(ata_cmd_unmatched, &tmp_ata_info);
247       /* woo hoo we could, so no need to store this in unmatched any more,
248          move both request and response to the matched table */
249       if(ata_info){
250         ata_info->response_frame=pinfo->fd->num;
251         g_hash_table_remove(ata_cmd_unmatched, ata_info);
252         g_hash_table_insert(ata_cmd_matched, GUINT_TO_POINTER(ata_info->request_frame), ata_info);
253         g_hash_table_insert(ata_cmd_matched, GUINT_TO_POINTER(ata_info->response_frame), ata_info);
254       }
255     }
256   } else {
257     ata_info=g_hash_table_lookup(ata_cmd_matched, GUINT_TO_POINTER(pinfo->fd->num));
258   }
259
260   if(ata_info){
261     if(response){
262       if(ata_info->request_frame){
263         nstime_t delta_ts;
264         tmp_item=proto_tree_add_uint(tree, hf_aoe_response_to, tvb, 0, 0, ata_info->request_frame);
265         PROTO_ITEM_SET_GENERATED(tmp_item);
266                 nstime_delta(&delta_ts, &pinfo->fd->abs_ts, &ata_info->req_time);
267         tmp_item=proto_tree_add_time(tree, hf_aoe_time, tvb, offset, 0, &delta_ts);
268         PROTO_ITEM_SET_GENERATED(tmp_item);
269       }
270     } else {
271       if(ata_info->response_frame){
272         tmp_item=proto_tree_add_uint(tree, hf_aoe_response_in, tvb, 0, 0, ata_info->response_frame);
273         PROTO_ITEM_SET_GENERATED(tmp_item);
274       }
275     }
276   }
277
278   /* aflags */
279   aflags=tvb_get_guint8(tvb, offset);
280   proto_tree_add_item(tree, hf_aoe_aflags_e, tvb, offset, 1, ENC_BIG_ENDIAN);
281   if(aflags&AOE_AFLAGS_E){
282     proto_tree_add_item(tree, hf_aoe_aflags_d, tvb, offset, 1, ENC_BIG_ENDIAN);
283   }
284   if(aflags&AOE_AFLAGS_W){
285     proto_tree_add_item(tree, hf_aoe_aflags_a, tvb, offset, 1, ENC_BIG_ENDIAN);
286   }
287   proto_tree_add_item(tree, hf_aoe_aflags_w, tvb, offset, 1, ENC_BIG_ENDIAN);
288   offset++;
289
290   /* err/feature */
291   proto_tree_add_item(tree, hf_aoe_err_feature, tvb, offset, 1, ENC_BIG_ENDIAN);
292   offset++;
293
294   /* sector count */
295   proto_tree_add_item(tree, hf_aoe_sector_count, tvb, offset, 1, ENC_BIG_ENDIAN);
296   offset++;
297
298   /* ata command/status */
299   if(!response){
300     proto_tree_add_item(tree, hf_aoe_acmd, tvb, offset, 1, ENC_BIG_ENDIAN);
301     col_append_fstr(pinfo->cinfo, COL_INFO, " ATA:%s", val_to_str(tvb_get_guint8(tvb, offset), ata_cmd_vals, " Unknown ATA<0x%02x>"));
302   } else {
303     proto_tree_add_item(tree, hf_aoe_astatus, tvb, offset, 1, ENC_BIG_ENDIAN);
304     if(ata_info != NULL && ata_info->request_frame){
305       /* we dont know what command it was unless we saw the request_frame */
306       tmp_item=proto_tree_add_uint(tree, hf_aoe_acmd, tvb, 0, 0, ata_info->cmd);
307       PROTO_ITEM_SET_GENERATED(tmp_item);
308       col_append_fstr(pinfo->cinfo, COL_INFO, " ATA:%s", val_to_str(ata_info->cmd, ata_cmd_vals, " Unknown ATA<0x%02x>"));
309     }
310   }
311   offset++;
312
313   /*lba   probably complete wrong */
314   lba=tvb_get_letohs(tvb, offset+4);
315   lba=(lba<<32)|tvb_get_letohl(tvb, offset);
316   offset+=8;
317   proto_tree_add_uint64(tree, hf_aoe_lba, tvb, offset-8, 6, lba);
318
319 }
320
321 static void
322 dissect_aoe_v1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
323 {
324   guint8 flags, cmd;
325   guint32 tag;
326   proto_item *flags_item=NULL;
327   proto_tree *flags_tree=NULL;
328
329   /* read and dissect the flags */
330   flags=tvb_get_guint8(tvb, 0)&0x0f;
331   if(tree){
332     flags_item=proto_tree_add_text(tree, tvb, 0, 1, "Flags:");
333     flags_tree=proto_item_add_subtree(flags_item, ett_aoe_flags);
334   }
335   proto_tree_add_item(flags_tree, hf_aoe_flags_response, tvb, 0, 1, ENC_BIG_ENDIAN);
336   proto_tree_add_item(flags_tree, hf_aoe_flags_error, tvb, 0, 1, ENC_BIG_ENDIAN);
337   if(flags_item){
338     proto_item_append_text(flags_item,(flags&AOE_FLAGS_RESPONSE)?" Response":" Request");
339     if(flags&AOE_FLAGS_ERROR){
340       proto_item_append_text(flags_item, " Error");
341     }
342   }
343
344
345   /* error */
346   if(flags&AOE_FLAGS_ERROR){
347     proto_tree_add_item(tree, hf_aoe_error, tvb, 1, 1, ENC_BIG_ENDIAN);
348         col_append_fstr(pinfo->cinfo, COL_INFO, "Error:%s ", val_to_str(tvb_get_guint8(tvb, 1), error_vals, "Unknown error<%d>"));
349   }
350
351   /* major/minor address */
352   proto_tree_add_item(tree, hf_aoe_major, tvb, 2, 2, ENC_BIG_ENDIAN);
353   proto_tree_add_item(tree, hf_aoe_minor, tvb, 4, 1, ENC_BIG_ENDIAN);
354
355   /* command */
356   cmd=tvb_get_guint8(tvb, 5);
357   proto_tree_add_item(tree, hf_aoe_cmd, tvb, 5, 1, ENC_BIG_ENDIAN);
358   col_append_fstr(pinfo->cinfo, COL_INFO, "%s %s", val_to_str(cmd, cmd_vals, "Unknown command<%d>"), (flags&AOE_FLAGS_RESPONSE)?"Response":"Request");
359
360
361   /* tag */
362   tag=tvb_get_letohl(tvb, 6);
363   proto_tree_add_item(tree, hf_aoe_tag, tvb, 6, 4, ENC_BIG_ENDIAN);
364
365
366   switch(cmd){
367   case AOE_CMD_ISSUE_ATA_COMMAND:
368     dissect_ata_pdu(pinfo, tree, tvb, 10, flags&AOE_FLAGS_RESPONSE, tag);
369     break;
370   case AOE_CMD_QUERY_CONFIG_INFO:
371     break;
372   }
373
374 }
375
376 static void
377 dissect_aoe(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
378 {
379   proto_item *item=NULL;
380   proto_tree *tree=NULL;
381   guint8 version;
382
383   col_set_str(pinfo->cinfo, COL_PROTOCOL, "AoE");
384   col_clear(pinfo->cinfo, COL_INFO);
385
386   if (parent_tree) {
387     item = proto_tree_add_item(parent_tree, proto_aoe, tvb, 0, -1, ENC_NA);
388     tree = proto_item_add_subtree(item, ett_aoe);
389   }
390
391   version=tvb_get_guint8(tvb, 0)>>4;
392   proto_tree_add_uint(tree, hf_aoe_version, tvb, 0, 1, version);
393   switch(version){
394   case 1:
395     dissect_aoe_v1(tvb, pinfo, tree);
396     break;
397   }
398 }
399
400 static void
401 ata_init(void)
402 {
403   if(ata_cmd_unmatched){
404     g_hash_table_destroy(ata_cmd_unmatched);
405     ata_cmd_unmatched=NULL;
406   }
407   ata_cmd_unmatched=g_hash_table_new(ata_cmd_hash_unmatched, ata_cmd_equal_unmatched);
408
409   if(ata_cmd_matched){
410     g_hash_table_destroy(ata_cmd_matched);
411     ata_cmd_matched=NULL;
412   }
413   ata_cmd_matched=g_hash_table_new(ata_cmd_hash_matched, ata_cmd_equal_matched);
414
415 }
416
417 void
418 proto_register_aoe(void)
419 {
420
421   static hf_register_info hf[] = {
422     { &hf_aoe_cmd,
423       { "Command", "aoe.cmd", FT_UINT8, BASE_DEC, VALS(cmd_vals), 0x0,
424         "AOE Command", HFILL}},
425     { &hf_aoe_version,
426       { "Version", "aoe.version", FT_UINT8, BASE_DEC, NULL, 0x0,
427         "Version of the AOE protocol", HFILL}},
428     { &hf_aoe_error,
429       { "Error", "aoe.error", FT_UINT8, BASE_DEC, VALS(error_vals), 0x0,
430         "Error code", HFILL}},
431     { &hf_aoe_err_feature,
432       { "Err/Feature", "aoe.err_feature", FT_UINT8, BASE_HEX, NULL, 0x0,
433         NULL, HFILL}},
434     { &hf_aoe_sector_count,
435       { "Sector Count", "aoe.sector_count", FT_UINT8, BASE_DEC, NULL, 0x0,
436         NULL, HFILL}},
437     { &hf_aoe_flags_response,
438       { "Response flag", "aoe.response", FT_BOOLEAN, 8, TFS(&tfs_response), AOE_FLAGS_RESPONSE, "Whether this is a response PDU or not", HFILL}},
439     { &hf_aoe_flags_error,
440       { "Error flag", "aoe.flags_error", FT_BOOLEAN, 8, TFS(&tfs_error), AOE_FLAGS_ERROR, "Whether this is an error PDU or not", HFILL}},
441     { &hf_aoe_major,
442       { "Major", "aoe.major", FT_UINT16, BASE_HEX, NULL, 0x0,
443         "Major address", HFILL}},
444     { &hf_aoe_minor,
445       { "Minor", "aoe.minor", FT_UINT8, BASE_HEX, NULL, 0x0,
446         "Minor address", HFILL}},
447     { &hf_aoe_acmd,
448       { "ATA Cmd", "aoe.ata.cmd", FT_UINT8, BASE_HEX, VALS(ata_cmd_vals), 0x0,
449         "ATA command opcode", HFILL}},
450     { &hf_aoe_astatus,
451       { "ATA Status", "aoe.ata.status", FT_UINT8, BASE_HEX, NULL, 0x0,
452         "ATA status bits", HFILL}},
453     { &hf_aoe_tag,
454       { "Tag", "aoe.tag", FT_UINT32, BASE_HEX, NULL, 0x0,
455         "Command Tag", HFILL}},
456     { &hf_aoe_aflags_e,
457       { "E", "aoe.aflags.e", FT_BOOLEAN, 8, TFS(&tfs_aflags_e), AOE_AFLAGS_E, "Whether this is a normal or LBA48 command", HFILL}},
458     { &hf_aoe_aflags_d,
459       { "D", "aoe.aflags.d", FT_BOOLEAN, 8, TFS(&tfs_aflags_d), AOE_AFLAGS_D, NULL, HFILL}},
460     { &hf_aoe_aflags_a,
461       { "A", "aoe.aflags.a", FT_BOOLEAN, 8, TFS(&tfs_aflags_a), AOE_AFLAGS_A, "Whether this is an asynchronous write or not", HFILL}},
462     { &hf_aoe_aflags_w,
463       { "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}},
464     { &hf_aoe_lba,
465       { "Lba", "aoe.lba", FT_UINT64, BASE_HEX, NULL, 0x00, "Lba address", HFILL}},
466     { &hf_aoe_response_in,
467       { "Response In", "aoe.response_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "The response to this packet is in this frame", HFILL }},
468     { &hf_aoe_response_to,
469       { "Response To", "aoe.response_to", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "This is a response to the ATA command in this frame", HFILL }},
470     { &hf_aoe_time,
471       { "Time from request", "aoe.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0, "Time between Request and Reply for ATA calls", HFILL }},
472   };
473
474   static gint *ett[] = {
475     &ett_aoe,
476     &ett_aoe_flags,
477   };
478
479   proto_aoe = proto_register_protocol("ATAoverEthernet", "AOE", "aoe");
480   proto_register_field_array(proto_aoe, hf, array_length(hf));
481   proto_register_subtree_array(ett, array_length(ett));
482
483   register_dissector("aoe", dissect_aoe, proto_aoe);
484   register_init_routine(ata_init);
485 }
486
487 void
488 proto_reg_handoff_aoe(void)
489 {
490   dissector_handle_t aoe_handle;
491
492   aoe_handle = find_dissector("aoe");
493   dissector_add_uint("ethertype", ETHERTYPE_AOE, aoe_handle);
494
495 }