From Matt Deckard:
[obnox/wireshark/wip.git] / epan / dissectors / packet-dis-fields.c
1 /* packet-dis-fields.c
2  * Routines and definitions for DIS field parsing.
3  * Copyright 2005, Scientific Research Corporation
4  * Initial implementation by Jeremy Ouellette <jouellet@scires.com>
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <string.h>
32 #include <epan/packet.h>
33 #include "packet-dis-fields.h"
34 #include "packet-dis-enums.h"
35
36 guint32 pduType;
37 guint32 entityKind;
38 guint32 entityDomain;
39 guint32 variableDatumLength;
40
41 DIS_ParserNode DIS_FIELDS_PDU_HEADER[] =
42 {
43     { DIS_FIELDTYPE_PROTOCOL_VERSION, "Protocol Version",0,0,0 },
44     { DIS_FIELDTYPE_UINT8,            "Exercise ID",0,0,0 },
45     { DIS_FIELDTYPE_PDU_TYPE,         "PDU Type",0,0,&pduType },
46     { DIS_FIELDTYPE_PROTOCOL_FAMILY,  "Protocol Family",0,0,0 },
47     { DIS_FIELDTYPE_TIMESTAMP,        "Timestamp",0,0,0 },
48     { DIS_FIELDTYPE_UINT16,           "Length",0,0,0 },
49     { DIS_FIELDTYPE_PAD16,            "Padding",0,0,0 },
50     { DIS_FIELDTYPE_END,              NULL,0,0,0 }
51 };
52
53 DIS_ParserNode DIS_FIELDS_ENTITY_ID[] =
54 {
55     { DIS_FIELDTYPE_UINT16, "Site",0,0,0 },
56     { DIS_FIELDTYPE_UINT16, "Application",0,0,0 },
57     { DIS_FIELDTYPE_UINT16, "Entity",0,0,0 },
58     { DIS_FIELDTYPE_END,    NULL,0,0,0 }
59 };
60
61 DIS_ParserNode DIS_FIELDS_ENTITY_TYPE[] =
62 {
63     { DIS_FIELDTYPE_ENTITY_KIND, "Entity Kind",0,0,&entityKind },
64     { DIS_FIELDTYPE_DOMAIN,      "Domain",0,0,&entityDomain },
65     { DIS_FIELDTYPE_COUNTRY,     "Country",0,0,0 },
66     { DIS_FIELDTYPE_CATEGORY,    "Category",0,0,0 },
67     { DIS_FIELDTYPE_SUBCATEGORY, "Subcategory",0,0,0 },
68     { DIS_FIELDTYPE_SPECIFIC,    "Specific",0,0,0 },
69     { DIS_FIELDTYPE_EXTRA,       "Extra",0,0,0 },
70     { DIS_FIELDTYPE_END,         NULL,0,0,0 }
71 };
72
73 DIS_ParserNode DIS_FIELDS_EVENT_ID[] =
74 {
75     { DIS_FIELDTYPE_UINT16, "Site",0,0,0 },
76     { DIS_FIELDTYPE_UINT16, "Application",0,0,0 },
77     { DIS_FIELDTYPE_UINT16, "Event Number",0,0,0 },
78     { DIS_FIELDTYPE_END,    NULL,0,0,0 }
79 };
80
81 DIS_ParserNode DIS_FIELDS_LINEAR_VELOCITY[] =
82 {
83     { DIS_FIELDTYPE_FLOAT32, "X",0,0,0 },
84     { DIS_FIELDTYPE_FLOAT32, "Y",0,0,0 },
85     { DIS_FIELDTYPE_FLOAT32, "Z",0,0,0 },
86     { DIS_FIELDTYPE_END,     NULL,0,0,0 }
87 };
88
89 DIS_ParserNode DIS_FIELDS_LOCATION_WORLD[] =
90 {
91     { DIS_FIELDTYPE_FLOAT64, "X",0,0,0 },
92     { DIS_FIELDTYPE_FLOAT64, "Y",0,0,0 },
93     { DIS_FIELDTYPE_FLOAT64, "Z",0,0,0 },
94     { DIS_FIELDTYPE_END,     NULL,0,0,0 }
95 };
96
97 DIS_ParserNode DIS_FIELDS_LOCATION_ENTITY[] =
98 {
99     { DIS_FIELDTYPE_FLOAT32, "X",0,0,0 },
100     { DIS_FIELDTYPE_FLOAT32, "Y",0,0,0 },
101     { DIS_FIELDTYPE_FLOAT32, "Z",0,0,0 },
102     { DIS_FIELDTYPE_END,     NULL,0,0,0 }
103 };
104
105 DIS_ParserNode DIS_FIELDS_ORIENTATION[] =
106 {
107     { DIS_FIELDTYPE_FLOAT32, "Psi",0,0,0 },
108     { DIS_FIELDTYPE_FLOAT32, "Theta",0,0,0 },
109     { DIS_FIELDTYPE_FLOAT32, "Phi",0,0,0 },
110     { DIS_FIELDTYPE_END,     NULL,0,0,0 }
111 };
112
113 DIS_ParserNode DIS_FIELDS_BURST_DESCRIPTOR[] =
114 {
115     { DIS_FIELDTYPE_ENTITY_TYPE, "Munition",0,0,0 },
116     { DIS_FIELDTYPE_WARHEAD,     "Warhead",0,0,0 },
117     { DIS_FIELDTYPE_FUSE,        "Fuse",0,0,0 },
118     { DIS_FIELDTYPE_UINT16,      "Quantity",0,0,0 },
119     { DIS_FIELDTYPE_UINT16,      "Rate",0,0,0 },
120     { DIS_FIELDTYPE_END,         NULL,0,0,0 }
121 };
122
123 DIS_ParserNode DIS_FIELDS_ARTICULATION_PARAMETER[] =
124 {
125     { DIS_FIELDTYPE_ARTIC_PARAM_TYPE_DESIGNATOR, "Parameter Type Designator",0,0,0 },
126     { DIS_FIELDTYPE_UINT8,                       "Change",0,0,0 },
127     { DIS_FIELDTYPE_UINT16,                      "Part Attached To ID",0,0,0 },
128     { DIS_FIELDTYPE_ARTIC_PARAM_TYPE,            "Parameter Type",0,0,0 },
129     { DIS_FIELDTYPE_UINT64,                      "Parameter Value",0,0,0 },
130     { DIS_FIELDTYPE_END,                         NULL,0,0,0 }
131 };
132
133 DIS_ParserNode DIS_FIELDS_NONE[] =
134 {
135     { DIS_FIELDTYPE_END, NULL, 0,0,0 }
136 };
137
138 DIS_ParserNode DIS_FIELDS_FIXED_DATUM[] =
139 {
140     { DIS_FIELDTYPE_DATUM_ID,                "Datum ID",0,0,0 },
141     { DIS_FIELDTYPE_FIXED_DATUM_VALUE,       "Datum value",0,0,0 },
142     { DIS_FIELDTYPE_END,                     NULL,0,0,0 }
143 };
144
145 DIS_ParserNode DIS_FIELDS_VARIABLE_DATUM[] =
146 {
147     { DIS_FIELDTYPE_DATUM_ID,                "Datum ID",0,0,0 },
148     { DIS_FIELDTYPE_DATUM_LENGTH,            "Datum length",0,0,&variableDatumLength },
149     { DIS_FIELDTYPE_VARIABLE_DATUM_VALUE,    "Datum value",0,0,0 },
150     { DIS_FIELDTYPE_END,                     NULL,0,0,0 }
151 };
152
153 DIS_BitMask DIS_APPEARANCE_LANDPLATFORM[] =
154 {
155     { 0x00000001, 0, "Paint Scheme", {
156         { 0, "Uniform color" },
157         { 1, "Camouflage" },
158         { 0,0 }
159     } },
160     { 0x00000002, 1, "Mobility", {
161         { 0, "No mobility kill" },
162         { 1, "Mobility kill" },
163         { 0,0 }
164     } },
165     { 0x00000004, 2, "Fire Power", {
166         { 0, "No fire-power kill" },
167         { 1, "Fire-power kill" },
168         { 0,0 }
169     } },
170     { 0x00000018, 3, "Damage", {
171         { 0, "No damage" },
172         { 1, "Slight damage" },
173         { 2, "Moderate damage" },
174         { 3, "Destroyed" },
175         { 0,0 }
176     } },
177     { 0, 0, 0, {
178         { 0, 0 }
179     } }
180 };
181
182 DIS_BitMask DIS_APPEARANCE_LIFEFORM[] =
183 {
184     { 0x00000001, 0, "Paint Scheme", {
185         { 0, "Uniform color" },
186         { 1, "Camouflage" },
187         { 0,0 }
188     } },
189     { 0x00000018, 3, "Health", {
190         { 0, "No injury" },
191         { 1, "Slight injury" },
192         { 2, "Moderate injury" },
193         { 3, "Fatal injury" },
194         { 0,0 }
195     } },
196     { 0, 0, 0, {
197         { 0, 0 }
198     } }
199 };
200
201 /* Adjust an offset variable for proper alignment for a specified field length.
202  */
203 static gint alignOffset(gint offset, guint fieldLength)
204 {
205     gint remainder = offset % fieldLength;
206     if (remainder != 0)
207     {
208         offset += fieldLength - remainder;
209     }
210     return offset;
211 }
212
213 /* Parse a field consisting of a specified number of bytes.  This field parser
214  * doesn't perform any alignment.
215  */
216 gint parseField_Bytes(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode, guint numBytes)
217 {
218     proto_tree_add_text(tree, tvb, offset, numBytes, "%s (%d bytes)",
219         parserNode.fieldLabel, numBytes);
220     offset += numBytes;
221     return offset;
222 }
223
224 /* Parse a bitmask field.
225  */
226 gint parseField_Bitmask(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode, guint numBytes)
227 {
228     DIS_BitMask *bitMask = 0;
229     guint64 uintVal = 0;
230
231     offset = alignOffset(offset, numBytes);
232
233     switch(numBytes)
234     {
235     case 1:
236         uintVal = tvb_get_guint8(tvb, offset);
237         break;
238     case 2:
239         uintVal = tvb_get_ntohs(tvb, offset);
240         break;
241     case 4:
242         uintVal = tvb_get_ntohl(tvb, offset);
243         break;
244     case 8:
245         uintVal = tvb_get_ntoh64(tvb, offset);
246         break;
247     default:
248         /* assert */
249         break;
250     }
251
252     switch(parserNode.fieldType)
253     {
254     case DIS_FIELDTYPE_APPEARANCE:
255         if ((entityKind == DIS_ENTITYKIND_PLATFORM) &&
256             (entityDomain == DIS_DOMAIN_LAND))
257         {
258             bitMask = DIS_APPEARANCE_LANDPLATFORM;
259         }
260         else if (entityKind == DIS_ENTITYKIND_LIFE_FORM)
261         {
262             bitMask = DIS_APPEARANCE_LIFEFORM;
263         }
264         break;
265     default:
266         break;
267     }
268
269     if (bitMask != 0)
270     {
271         int maskIndex = 0;
272         while (bitMask[maskIndex].maskBits != 0)
273         {
274             int mapIndex = 0;
275             DIS_BitMaskMapping *bitMaskMap = bitMask[maskIndex].bitMappings;
276
277             while (bitMaskMap[mapIndex].label != 0)
278             {
279                 if (((bitMask[maskIndex].maskBits & uintVal) >> bitMask[maskIndex].shiftBits) ==
280                     bitMaskMap[mapIndex].value)
281                 {
282                     proto_tree_add_text(tree, tvb, offset, numBytes,
283                         "%s = %s", bitMask[maskIndex].label,
284                         bitMaskMap[mapIndex].label);
285                     break;
286                 }
287                 ++mapIndex;
288             }
289             ++maskIndex;
290         }
291     }
292     else
293     {
294         proto_tree_add_text(tree, tvb, offset, numBytes,
295             "Unknown Appearance Type (%" G_GINT64_MODIFIER "u)", uintVal);
296     }
297
298     offset += numBytes;
299
300     return offset;
301 }
302
303 /* Parse an unsigned integer field of a specified number of bytes.
304  */
305 gint parseField_UInt(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode, guint numBytes)
306 {
307     guint64 uintVal = 0;
308
309     offset = alignOffset(offset, numBytes);
310
311     switch(numBytes)
312     {
313     case 1:
314         uintVal = tvb_get_guint8(tvb, offset);
315         break;
316     case 2:
317         uintVal = tvb_get_ntohs(tvb, offset);
318         break;
319     case 4:
320         uintVal = tvb_get_ntohl(tvb, offset);
321         break;
322     case 8:
323         uintVal = tvb_get_ntoh64(tvb, offset);
324         break;
325     default:
326         /* assert */
327         break;
328     }
329
330     proto_tree_add_text(tree, tvb, offset, numBytes, "%s = %" G_GINT64_MODIFIER "u",
331         parserNode.fieldLabel, uintVal);
332
333     if (parserNode.outputVar != 0)
334     {
335         *(parserNode.outputVar) = (guint32)uintVal;
336     }
337
338     offset += numBytes;
339
340     return offset;
341 }
342
343 /* Parse a signed integer field of a specified number of bytes.
344  */
345 gint parseField_Int(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode, guint numBytes)
346 {
347     guint64 uintVal = 0;
348
349     offset = alignOffset(offset, numBytes);
350
351     switch(numBytes)
352     {
353     case 1:
354         uintVal = tvb_get_guint8(tvb, offset);
355         break;
356     case 2:
357         uintVal = tvb_get_ntohs(tvb, offset);
358         break;
359     case 4:
360         uintVal = tvb_get_ntohl(tvb, offset);
361         break;
362     case 8:
363         uintVal = tvb_get_ntoh64(tvb, offset);
364         break;
365     default:
366         /* assert */
367         break;
368     }
369
370     proto_tree_add_text(tree, tvb, offset, numBytes, "%s = %" G_GINT64_MODIFIER "d",
371         parserNode.fieldLabel, uintVal);
372
373     offset += numBytes;
374
375     return offset;
376 }
377
378 /* Parse a field that explicitly specified a number of pad bytes (vs implicit
379  * padding, which occurs whenever padding is inserted to properly align the
380  * field.
381  */
382 gint parseField_Pad(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode _U_, guint numBytes)
383 {
384     proto_tree_add_text(tree, tvb, offset, numBytes,
385         "Explicit Padding (%d bytes)", numBytes);
386
387     offset += numBytes;
388
389     return offset;
390 }
391
392 /* Parse an enumerated type field.
393  */
394 gint parseField_Enum(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode, guint numBytes)
395 {
396     const value_string *enumStrings = 0;
397     guint32 enumVal = 0;
398     const gchar *enumStr = 0;
399
400     offset = alignOffset(offset, numBytes);
401
402     switch(parserNode.fieldType)
403     {
404     case DIS_FIELDTYPE_PROTOCOL_VERSION:
405         enumStrings = DIS_PDU_ProtocolVersion_Strings;
406         break;
407     case DIS_FIELDTYPE_PROTOCOL_FAMILY:
408         enumStrings = DIS_PDU_ProtocolFamily_Strings;
409         break;
410     case DIS_FIELDTYPE_PDU_TYPE:
411         enumStrings = DIS_PDU_Type_Strings;
412         break;
413     case DIS_FIELDTYPE_ENTITY_KIND:
414         enumStrings = DIS_PDU_EntityKind_Strings;
415         break;
416     case DIS_FIELDTYPE_DOMAIN:
417         enumStrings = DIS_PDU_Domain_Strings;
418         break;
419     case DIS_FIELDTYPE_DETONATION_RESULT:
420         enumStrings = DIS_PDU_DetonationResult_Strings;
421         break;
422     case DIS_FIELDTYPE_CATEGORY:
423         if (entityKind == DIS_ENTITYKIND_PLATFORM)
424         {
425             switch(entityDomain)
426             {
427             case DIS_DOMAIN_LAND:
428                 enumStrings = DIS_PDU_Category_LandPlatform_Strings;
429                 break;
430             case DIS_DOMAIN_AIR:
431                 enumStrings = DIS_PDU_Category_AirPlatform_Strings;
432                 break;
433             case DIS_DOMAIN_SURFACE:
434                 enumStrings = DIS_PDU_Category_SurfacePlatform_Strings;
435                 break;
436             case DIS_DOMAIN_SUBSURFACE:
437                 enumStrings = DIS_PDU_Category_SubsurfacePlatform_Strings;
438                 break;
439             case DIS_DOMAIN_SPACE:
440                 enumStrings = DIS_PDU_Category_SpacePlatform_Strings;
441                 break;
442             default:
443                 enumStrings = 0;
444                 break;
445             }
446         }
447         break;
448     default:
449         enumStrings = 0;
450         break; 
451     }
452
453     switch(numBytes)
454     {
455     case 1:
456         enumVal = tvb_get_guint8(tvb, offset);
457         break;
458     case 2:
459         enumVal = tvb_get_ntohs(tvb, offset);
460         break;
461     case 4:
462         enumVal = tvb_get_ntohl(tvb, offset);
463         break;
464     default:
465         /* assert */
466         break;
467     }
468
469     if (enumStrings != 0)
470     {
471         enumStr = val_to_str(enumVal, enumStrings, "Unknown Enum Value");
472     }
473     else
474     {
475         enumStr = "Unknown Enum Type";
476     }
477
478     proto_tree_add_text(tree, tvb, offset, numBytes, "%s = %s",
479         parserNode.fieldLabel, enumStr);
480
481     if (parserNode.outputVar != 0)
482     {
483         *(parserNode.outputVar) = enumVal;
484     }
485
486     offset += numBytes;
487
488     return offset;
489 }
490
491 /* Parse a 4-byte floating-point value.
492  */
493 gint parseField_Float(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode)
494 {
495     gfloat floatVal;
496
497     offset = alignOffset(offset, 4);
498     floatVal = tvb_get_ntohieee_float(tvb, offset);
499     proto_tree_add_text(tree, tvb, offset, 4, "%s = %f",
500         parserNode.fieldLabel, floatVal);
501
502     offset += 4;
503
504     return offset;
505 }
506
507 /* Parse an 8-byte floating-point value.
508  */
509 gint parseField_Double(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode)
510 {
511     gdouble doubleVal;
512
513     offset = alignOffset(offset, 8);
514     doubleVal = tvb_get_ntohieee_double(tvb, offset);
515     proto_tree_add_text(tree, tvb, offset, 8, "%s = %lf",
516         parserNode.fieldLabel, doubleVal);
517
518     offset += 8;
519
520     return offset;
521 }
522
523 /* Parse the Timestamp */
524 gint parseField_Timestamp(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode)
525 {
526    /* some consts */
527    static double MSEC_PER_SECOND = 1000.0;
528    static double MSEC_PER_MINUTE = 60.0 * 1000.0 ;
529    static double MSEC_PER_HOUR = 60.0 * 60.0 * 1000.0;
530    static double FSV = 0x7fffffff;
531    /* variables */
532    guint isAbsolute = 0;
533    guint32 uintVal;
534    guint minutes;
535    guint seconds;
536    guint milliseconds;
537    double ms;
538
539    offset = alignOffset(offset, 4);
540    
541    /* convert to host value */
542    uintVal = tvb_get_ntohl(tvb, offset);
543    /* determine absolute vis sim time */
544    if( uintVal & 1 )
545       isAbsolute = 1;
546
547    /* convert TS to MS */
548    ms = (uintVal >> 1) * MSEC_PER_HOUR / FSV;
549    ms += 0.5;
550
551    /* calc minutes and reduce ms */
552    minutes = (guint) (ms / MSEC_PER_MINUTE);
553    ms -= (minutes * MSEC_PER_MINUTE);
554
555    /* calc seconds and reduce ms */
556    seconds = (guint) (ms / MSEC_PER_SECOND);
557    ms -= (seconds * MSEC_PER_SECOND);
558
559    /* truncate milliseconds */
560    milliseconds = (guint) ms;
561
562    /* push out the values */
563    if( isAbsolute )
564    {
565       proto_tree_add_text(tree, tvb, offset, 4, "%s = %02d:%02d %03d absolute (UTM)",
566             parserNode.fieldLabel, minutes, seconds, milliseconds);
567    }
568    else
569    {
570       proto_tree_add_text(tree, tvb, offset, 4, "%s = %02d:%02d %03d relative",
571             parserNode.fieldLabel, minutes, seconds, milliseconds);
572    }
573
574    offset += 4;
575    return offset;
576 }