Moved dissect_ndr_nt_NTTIME() from packet-dcerpc-samr.c to packet-dcerpc-nt.c
[obnox/wireshark/wip.git] / packet-atalk.c
1 /* packet-atalk.c
2  * Routines for Appletalk packet disassembly (DDP, currently).
3  *
4  * $Id: packet-atalk.c,v 1.62 2002/01/21 07:36:32 guy Exp $
5  *
6  * Simon Wilkinson <sxw@dcs.ed.ac.uk>
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@ethereal.com>
10  * Copyright 1998
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 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif
34
35 #ifdef HAVE_NETINET_IN_H
36 # include <netinet/in.h>
37 #endif
38
39 #include <glib.h>
40 #include <epan/packet.h>
41 #include "etypes.h"
42 #include "ppptypes.h"
43 #include "aftypes.h"
44 #include <epan/atalk-utils.h>
45
46 static int proto_llap = -1;
47 static int hf_llap_dst = -1;
48 static int hf_llap_src = -1;
49 static int hf_llap_type = -1;
50
51 static int proto_ddp = -1;
52 static int hf_ddp_hopcount = -1;
53 static int hf_ddp_len = -1;
54 static int hf_ddp_checksum = -1;
55 static int hf_ddp_dst_net = -1;
56 static int hf_ddp_src_net = -1;
57 static int hf_ddp_dst_node = -1;
58 static int hf_ddp_src_node = -1;
59 static int hf_ddp_dst_socket = -1;
60 static int hf_ddp_src_socket = -1;
61 static int hf_ddp_type = -1;
62
63 static int proto_nbp = -1;
64 static int hf_nbp_op = -1;
65 static int hf_nbp_info = -1;
66 static int hf_nbp_count = -1;
67 static int hf_nbp_tid = -1;
68
69 static int hf_nbp_node_net = -1;
70 static int hf_nbp_node_port = -1;
71 static int hf_nbp_node_node = -1;
72 static int hf_nbp_node_enum = -1;
73 static int hf_nbp_node_object = -1;
74 static int hf_nbp_node_type = -1;
75 static int hf_nbp_node_zone = -1;
76
77 static int proto_rtmp = -1;
78 static int hf_rtmp_net = -1;
79 static int hf_rtmp_node_len = -1;
80 static int hf_rtmp_node = -1;
81 static int hf_rtmp_tuple_net = -1;
82 static int hf_rtmp_tuple_range_start = -1;
83 static int hf_rtmp_tuple_range_end = -1;
84 static int hf_rtmp_tuple_dist = -1;
85 static int hf_rtmp_function = -1;
86
87 static gint ett_nbp = -1;
88 static gint ett_nbp_info = -1;
89 static gint ett_nbp_node = -1;
90 static gint ett_rtmp = -1;
91 static gint ett_rtmp_tuple = -1;
92 static gint ett_ddp = -1;
93 static gint ett_llap = -1;
94 static gint ett_pstring = -1;
95
96 static dissector_table_t ddp_dissector_table;
97
98 static dissector_handle_t data_handle;
99
100 #define DDP_SHORT_HEADER_SIZE 5
101
102 /*
103  * P = Padding, H = Hops, L = Len
104  *
105  * PPHHHHLL LLLLLLLL
106  *
107  * Assumes the argument is in host byte order.
108  */
109 #define ddp_hops(x)     ( ( x >> 10) & 0x3C )
110 #define ddp_len(x)              ( x & 0x03ff )
111 typedef struct _e_ddp {
112   guint16       hops_len; /* combines pad, hops, and len */
113   guint16       sum,dnet,snet;
114   guint8        dnode,snode;
115   guint8        dport,sport;
116   guint8        type;
117 } e_ddp;
118
119 #define DDP_HEADER_SIZE 13
120
121
122 static const value_string op_vals[] = {
123   {DDP_RTMPDATA, "AppleTalk Routing Table response or data" },
124   {DDP_NBP, "AppleTalk Name Binding Protocol packet"},
125   {DDP_ATP, "AppleTalk Transaction Protocol packet"},
126   {DDP_AEP, "AppleTalk Echo Protocol packet"},
127   {DDP_RTMPREQ, "AppleTalk Routing Table request"},
128   {DDP_ZIP, "AppleTalk Zone Information Protocol packet"},
129   {DDP_ADSP, "AppleTalk Data Stream Protocol"},
130   {DDP_EIGRP, "Cisco EIGRP for AppleTalk"},
131   {0, NULL}
132 };
133
134 static const value_string rtmp_function_vals[] = {
135   {1, "Request"},
136   {2, "Route Data Request (split horizon processed)"},
137   {3, "Route Data Request (no split horizon processing)"},
138   {0, NULL}
139 };
140
141 #define NBP_LOOKUP 2
142 #define NBP_FORWARD 4
143 #define NBP_REPLY 3
144
145 static const value_string nbp_op_vals[] = {
146   {NBP_LOOKUP, "lookup"},
147   {NBP_FORWARD, "forward request"},
148   {NBP_REPLY, "reply"},
149   {0, NULL}
150 };
151
152 /*
153  * XXX - do this with an FT_UINT_STRING?
154  * Unfortunately, you can't extract from an FT_UINT_STRING the string,
155  * which we'd want to do in order to put it into the "Data:" portion.
156  */
157 int dissect_pascal_string(tvbuff_t *tvb, int offset, proto_tree *tree,
158         int hf_index)
159 {
160         int len;
161         
162         len = tvb_get_guint8(tvb, offset);
163         offset++;
164
165         if ( tree )
166         {
167                 char *tmp;
168                 proto_tree *item;
169                 proto_tree *subtree;
170                 
171                 /*
172                  * XXX - if we could do this inside the protocol tree
173                  * code, we could perhaps avoid allocating and freeing
174                  * this string buffer.
175                  */
176                 tmp = g_malloc( len+1 );
177                 tvb_memcpy(tvb, tmp, offset, len);
178                 tmp[len] = 0;
179                 item = proto_tree_add_string(tree, hf_index, tvb, offset-1, len+1, tmp);
180
181                 subtree = proto_item_add_subtree(item, ett_pstring);
182                 proto_tree_add_text(subtree, tvb, offset-1, 1, "Length: %d", len);
183                 proto_tree_add_text(subtree, tvb, offset, len, "Data: %s", tmp);
184                 
185                 g_free(tmp);
186         }
187         offset += len;
188         
189         return offset;  
190 }
191
192 static void
193 dissect_rtmp_request(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
194   proto_tree *rtmp_tree;
195   proto_item *ti;
196   guint8 function;
197
198   if (check_col(pinfo->cinfo, COL_PROTOCOL))
199     col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP");
200   if (check_col(pinfo->cinfo, COL_INFO))
201     col_clear(pinfo->cinfo, COL_INFO);
202
203   function = tvb_get_guint8(tvb, 0);
204
205   if (check_col(pinfo->cinfo, COL_INFO))
206     col_add_fstr(pinfo->cinfo, COL_INFO, "%s",
207         val_to_str(function, rtmp_function_vals, "Unknown function (%02)"));
208   
209   if (tree) {
210     ti = proto_tree_add_item(tree, proto_rtmp, tvb, 0, 1, FALSE);
211     rtmp_tree = proto_item_add_subtree(ti, ett_rtmp);
212
213     proto_tree_add_uint(rtmp_tree, hf_rtmp_function, tvb, 0, 1, function);
214   }
215 }
216
217 static void
218 dissect_rtmp_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
219   proto_tree *rtmp_tree;
220   proto_item *ti;
221   int offset = 0;
222   guint16 net;
223   guint8 nodelen,nodelen_bits;
224   guint16 node; /* might be more than 8 bits */
225   int i;
226
227   if (check_col(pinfo->cinfo, COL_PROTOCOL))
228     col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTMP");
229   if (check_col(pinfo->cinfo, COL_INFO))
230     col_clear(pinfo->cinfo, COL_INFO);
231
232   net = tvb_get_ntohs(tvb, offset);
233   nodelen_bits = tvb_get_guint8(tvb, offset+2);
234   if ( nodelen_bits <= 8 ) {
235     node = tvb_get_guint8(tvb, offset)+1;
236     nodelen = 1;
237   } else {
238     node = tvb_get_ntohs(tvb, offset);
239     nodelen = 2;
240   }
241   
242   if (check_col(pinfo->cinfo, COL_INFO))
243     col_add_fstr(pinfo->cinfo, COL_INFO, "Net: %u  Node Len: %u  Node: %u",
244                 net, nodelen_bits, node);
245   
246   if (tree) {
247     ti = proto_tree_add_item(tree, proto_rtmp, tvb, offset, -1, FALSE);
248     rtmp_tree = proto_item_add_subtree(ti, ett_rtmp);
249
250     proto_tree_add_uint(rtmp_tree, hf_rtmp_net, tvb, offset, 2, net);
251     proto_tree_add_uint(rtmp_tree, hf_rtmp_node_len, tvb, offset+2, 1,
252                         nodelen_bits);
253     proto_tree_add_uint(rtmp_tree, hf_rtmp_node, tvb, offset+3, nodelen,
254                         node);
255     offset += 3 + nodelen;
256
257     i = 1;
258     while (tvb_offset_exists(tvb, offset)) {
259       proto_tree *tuple_item, *tuple_tree;
260       guint16 tuple_net;
261       guint8 tuple_dist;
262       guint16 tuple_range_end;
263
264       tuple_net = tvb_get_ntohs(tvb, offset);
265       tuple_dist = tvb_get_guint8(tvb, offset+2);
266
267       if (tuple_dist & 0x80) {
268         tuple_range_end = tvb_get_ntohs(tvb, offset+3);
269         tuple_item = proto_tree_add_text(rtmp_tree, tvb, offset, 6,
270                         "Tuple %d:  Range Start: %u  Dist: %u  Range End: %u",
271                         i, tuple_net, tuple_dist&0x7F, tuple_range_end);
272       } else {
273         tuple_item = proto_tree_add_text(rtmp_tree, tvb, offset, 3,
274                         "Tuple %d:  Net: %u  Dist: %u",
275                         i, tuple_net, tuple_dist);
276       }
277       tuple_tree = proto_item_add_subtree(tuple_item, ett_rtmp_tuple);
278
279       if (tuple_dist & 0x80) {
280         proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_range_start, tvb, offset, 2, 
281                         tuple_net);
282       } else {
283         proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_net, tvb, offset, 2, 
284                         tuple_net);
285       }
286       proto_tree_add_uint(tuple_tree, hf_rtmp_tuple_dist, tvb, offset+2, 1,
287                         tuple_dist & 0x7F);
288
289       if (tuple_dist & 0x80) {
290         /*
291          * Extended network tuple.
292          */
293         proto_tree_add_item(tuple_tree, hf_rtmp_tuple_range_end, tvb, offset+3, 2, 
294                                 FALSE);
295         offset += 6;
296       } else
297         offset += 3;
298
299       i++;
300     }
301   }
302 }
303
304 static void
305 dissect_nbp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
306   proto_tree *nbp_tree;
307   proto_tree *nbp_info_tree;
308   proto_item *ti, *info_item;
309   int offset = 0;
310   guint8 info;
311   guint op, count;
312   unsigned int i;
313
314   if (check_col(pinfo->cinfo, COL_PROTOCOL))
315     col_set_str(pinfo->cinfo, COL_PROTOCOL, "NBP");
316   if (check_col(pinfo->cinfo, COL_INFO))
317     col_clear(pinfo->cinfo, COL_INFO);
318
319   info = tvb_get_guint8(tvb, offset);
320   op = info >> 4;
321   count = info & 0x0F;
322
323   if (check_col(pinfo->cinfo, COL_INFO))
324     col_add_fstr(pinfo->cinfo, COL_INFO, "Op: %s  Count: %u",
325       val_to_str(op, nbp_op_vals, "Unknown (0x%01x)"), count);
326   
327   if (tree) {
328     ti = proto_tree_add_item(tree, proto_nbp, tvb, offset, -1, FALSE);
329     nbp_tree = proto_item_add_subtree(ti, ett_nbp);
330
331     info_item = proto_tree_add_uint_format(nbp_tree, hf_nbp_info, tvb, offset, 1,
332                 info,
333                 "Info: 0x%01X  Operation: %s  Count: %u", info,
334                 val_to_str(op, nbp_op_vals, "Unknown (0x%01X)"),
335                 count);
336     nbp_info_tree = proto_item_add_subtree(info_item, ett_nbp_info);
337     proto_tree_add_uint(nbp_info_tree, hf_nbp_op, tvb, offset, 1, info);
338     proto_tree_add_uint(nbp_info_tree, hf_nbp_count, tvb, offset, 1, info);
339     proto_tree_add_item(nbp_tree, hf_nbp_tid, tvb, offset+1, 1, FALSE);
340     offset += 2;
341
342     for (i=0; i<count; i++) {
343       proto_tree *node_item,*node_tree;
344       int soffset = offset;
345
346       node_item = proto_tree_add_text(nbp_tree, tvb, offset, -1, 
347                         "Node %d", i+1);
348       node_tree = proto_item_add_subtree(node_item, ett_nbp_node);
349
350       proto_tree_add_item(node_tree, hf_nbp_node_net, tvb, offset, 2, FALSE);
351       offset += 2;
352       proto_tree_add_item(node_tree, hf_nbp_node_node, tvb, offset, 1, FALSE);
353       offset++;
354       proto_tree_add_item(node_tree, hf_nbp_node_port, tvb, offset, 1, FALSE);
355       offset++;
356       proto_tree_add_item(node_tree, hf_nbp_node_enum, tvb, offset, 1, FALSE);
357       offset++;
358
359       offset = dissect_pascal_string(tvb, offset, node_tree, hf_nbp_node_object);
360       offset = dissect_pascal_string(tvb, offset, node_tree, hf_nbp_node_type);
361       offset = dissect_pascal_string(tvb, offset, node_tree, hf_nbp_node_zone);
362
363       proto_item_set_len(node_item, offset-soffset);
364     }
365   }
366
367   return;
368 }
369
370 static void
371 dissect_ddp_short(tvbuff_t *tvb, packet_info *pinfo, guint8 dnode,
372                   guint8 snode, proto_tree *tree)
373 {
374   guint16 len;
375   guint8  dport;
376   guint8  sport;
377   guint8  type;
378   proto_tree *ddp_tree = NULL;
379   proto_item *ti;
380   static struct atalk_ddp_addr src, dst;
381   tvbuff_t   *new_tvb;
382
383   if (check_col(pinfo->cinfo, COL_PROTOCOL))
384     col_set_str(pinfo->cinfo, COL_PROTOCOL, "DDP");
385   if (check_col(pinfo->cinfo, COL_INFO))
386     col_clear(pinfo->cinfo, COL_INFO);
387
388   if (tree) {
389     ti = proto_tree_add_item(tree, proto_ddp, tvb, 0, DDP_SHORT_HEADER_SIZE,
390                              FALSE);
391     ddp_tree = proto_item_add_subtree(ti, ett_ddp);
392   }
393   len = tvb_get_ntohs(tvb, 0);
394   if (tree)
395       proto_tree_add_uint(ddp_tree, hf_ddp_len, tvb, 0, 2, len);
396   dport = tvb_get_guint8(tvb, 2);
397   if (tree)
398     proto_tree_add_uint(ddp_tree, hf_ddp_dst_socket, tvb, 2, 1, dport);
399   sport = tvb_get_guint8(tvb, 3);
400   if (tree)
401     proto_tree_add_uint(ddp_tree, hf_ddp_src_socket, tvb, 3, 1, sport);
402   type = tvb_get_guint8(tvb, 4);
403   
404   src.net = 0;
405   src.node = snode;
406   src.port = sport;
407   dst.net = 0;
408   dst.node = dnode;
409   dst.port = dport;
410   SET_ADDRESS(&pinfo->net_src, AT_ATALK, sizeof src, (guint8 *)&src);
411   SET_ADDRESS(&pinfo->src, AT_ATALK, sizeof src, (guint8 *)&src);
412   SET_ADDRESS(&pinfo->net_dst, AT_ATALK, sizeof dst, (guint8 *)&dst);
413   SET_ADDRESS(&pinfo->dst, AT_ATALK, sizeof dst, (guint8 *)&dst);
414
415   if (check_col(pinfo->cinfo, COL_INFO)) {
416     col_add_str(pinfo->cinfo, COL_INFO,
417       val_to_str(type, op_vals, "Unknown DDP protocol (%02x)"));
418   }
419   if (tree)
420     proto_tree_add_uint(ddp_tree, hf_ddp_type, tvb, 4, 1, type);
421   
422   new_tvb = tvb_new_subset(tvb, DDP_SHORT_HEADER_SIZE, -1, -1);
423
424   if (!dissector_try_port(ddp_dissector_table, type, new_tvb, pinfo, tree))
425     call_dissector(data_handle,new_tvb, pinfo, tree);
426 }
427
428 static void
429 dissect_ddp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
430 {
431   e_ddp       ddp;
432   proto_tree *ddp_tree;
433   proto_item *ti;
434   static struct atalk_ddp_addr src, dst;
435   tvbuff_t   *new_tvb;
436
437   if (check_col(pinfo->cinfo, COL_PROTOCOL))
438     col_set_str(pinfo->cinfo, COL_PROTOCOL, "DDP");
439   if (check_col(pinfo->cinfo, COL_INFO))
440     col_clear(pinfo->cinfo, COL_INFO);
441
442   tvb_memcpy(tvb, (guint8 *)&ddp, 0, sizeof(e_ddp));
443   ddp.dnet=ntohs(ddp.dnet);
444   ddp.snet=ntohs(ddp.snet);
445   ddp.sum=ntohs(ddp.sum);
446   ddp.hops_len=ntohs(ddp.hops_len);
447   
448   src.net = ddp.snet;
449   src.node = ddp.snode;
450   src.port = ddp.sport;
451   dst.net = ddp.dnet;
452   dst.node = ddp.dnode;
453   dst.port = ddp.dport;
454   SET_ADDRESS(&pinfo->net_src, AT_ATALK, sizeof src, (guint8 *)&src);
455   SET_ADDRESS(&pinfo->src, AT_ATALK, sizeof src, (guint8 *)&src);
456   SET_ADDRESS(&pinfo->net_dst, AT_ATALK, sizeof dst, (guint8 *)&dst);
457   SET_ADDRESS(&pinfo->dst, AT_ATALK, sizeof dst, (guint8 *)&dst);
458
459   if (check_col(pinfo->cinfo, COL_INFO))
460     col_add_str(pinfo->cinfo, COL_INFO,
461       val_to_str(ddp.type, op_vals, "Unknown DDP protocol (%02x)"));
462   
463   if (tree) {
464     ti = proto_tree_add_item(tree, proto_ddp, tvb, 0, DDP_HEADER_SIZE,
465                              FALSE);
466     ddp_tree = proto_item_add_subtree(ti, ett_ddp);
467     proto_tree_add_uint(ddp_tree, hf_ddp_hopcount,   tvb, 0, 1,
468                         ddp_hops(ddp.hops_len));
469     proto_tree_add_uint(ddp_tree, hf_ddp_len,        tvb, 0, 2, 
470                         ddp_len(ddp.hops_len));
471     proto_tree_add_uint(ddp_tree, hf_ddp_checksum,   tvb, 2,  2,
472                         ddp.sum);
473     proto_tree_add_uint(ddp_tree, hf_ddp_dst_net,    tvb, 4,  2,
474                         ddp.dnet);
475     proto_tree_add_uint(ddp_tree, hf_ddp_src_net,    tvb, 6,  2,
476                         ddp.snet);
477     proto_tree_add_uint(ddp_tree, hf_ddp_dst_node,   tvb, 8,  1,
478                         ddp.dnode);
479     proto_tree_add_uint(ddp_tree, hf_ddp_src_node,   tvb, 9,  1,
480                         ddp.snode);
481     proto_tree_add_uint(ddp_tree, hf_ddp_dst_socket, tvb, 10, 1,
482                         ddp.dport);
483     proto_tree_add_uint(ddp_tree, hf_ddp_src_socket, tvb, 11, 1,
484                         ddp.sport);
485     proto_tree_add_uint(ddp_tree, hf_ddp_type,       tvb, 12, 1,
486                         ddp.type);  
487   }
488
489   new_tvb = tvb_new_subset(tvb, DDP_HEADER_SIZE, -1, -1);
490
491   if (!dissector_try_port(ddp_dissector_table, ddp.type, new_tvb, pinfo, tree))
492     call_dissector(data_handle,new_tvb, pinfo, tree);
493 }
494
495 static const value_string llap_type_vals[] = {
496   {0x01, "Short DDP"},
497   {0x02, "DDP" },
498   {0x81, "Enquiry"},
499   {0x82, "Acknowledgement"},
500   {0x84, "RTS"},
501   {0x85, "CTS"},
502   {0, NULL}
503 };
504
505 void
506 capture_llap(const u_char *pd, int len, packet_counts *ld)
507 {
508   ld->other++;
509 }
510
511 static void
512 dissect_llap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
513 {
514   guint8 dnode;
515   guint8 snode;
516   guint8 type;
517   proto_tree *llap_tree = NULL;
518   proto_item *ti;
519   tvbuff_t   *new_tvb;
520
521   if (check_col(pinfo->cinfo, COL_PROTOCOL))
522     col_set_str(pinfo->cinfo, COL_PROTOCOL, "LLAP");
523   if (check_col(pinfo->cinfo, COL_INFO))
524     col_clear(pinfo->cinfo, COL_INFO);
525
526   if (tree) {
527     ti = proto_tree_add_item(tree, proto_llap, tvb, 0, 3, FALSE);
528     llap_tree = proto_item_add_subtree(ti, ett_llap);
529   }
530
531   dnode = tvb_get_guint8(tvb, 0);
532   if (tree)  
533     proto_tree_add_uint(llap_tree, hf_llap_dst, tvb, 0, 1, dnode);
534   snode = tvb_get_guint8(tvb, 1);
535   if (tree)
536     proto_tree_add_uint(llap_tree, hf_llap_src, tvb, 1, 1, snode);
537   type = tvb_get_guint8(tvb, 2);
538   if (check_col(pinfo->cinfo, COL_INFO)) {
539     col_add_str(pinfo->cinfo, COL_INFO,
540       val_to_str(type, llap_type_vals, "Unknown LLAP type (%02x)"));
541   }
542   if (tree)
543     proto_tree_add_uint(llap_tree, hf_llap_type, tvb, 2, 1, type);
544   
545   new_tvb = tvb_new_subset(tvb, 3, -1, -1);
546
547   if (proto_is_protocol_enabled(proto_ddp)) {
548     pinfo->current_proto = "DDP";
549     switch (type) {
550
551     case 0x01:
552       dissect_ddp_short(new_tvb, pinfo, dnode, snode, tree);
553       return;
554
555     case 0x02:
556       dissect_ddp(new_tvb, pinfo, tree);
557       return;
558     }
559   }
560   call_dissector(data_handle,new_tvb, pinfo, tree);
561 }
562
563 void
564 proto_register_atalk(void)
565 {
566   static hf_register_info hf_llap[] = {
567     { &hf_llap_dst,
568       { "Destination Node",     "llap.dst",     FT_UINT8,  BASE_DEC, NULL, 0x0,
569         "", HFILL }},
570
571     { &hf_llap_src,
572       { "Source Node",          "llap.src",     FT_UINT8,  BASE_DEC, NULL, 0x0,
573         "", HFILL }},
574
575     { &hf_llap_type,
576       { "Type",                 "llap.type",    FT_UINT8,  BASE_HEX, VALS(llap_type_vals), 0x0,
577         "", HFILL }},
578   };
579
580   static hf_register_info hf_ddp[] = {
581     { &hf_ddp_hopcount,
582       { "Hop count",            "ddp.hopcount", FT_UINT8,  BASE_DEC, NULL, 0x0,
583         "", HFILL }},
584
585     { &hf_ddp_len,
586       { "Datagram length",      "ddp.len",      FT_UINT16, BASE_DEC, NULL, 0x0,
587         "", HFILL }},
588
589     { &hf_ddp_checksum,
590       { "Checksum",             "ddp.checksum", FT_UINT16, BASE_DEC, NULL, 0x0,
591         "", HFILL }},
592
593     { &hf_ddp_dst_net,
594       { "Destination Net",      "ddp.dst.net",  FT_UINT16, BASE_DEC, NULL, 0x0,
595         "", HFILL }},
596
597     { &hf_ddp_src_net,
598       { "Source Net",           "ddp.src.net",  FT_UINT16, BASE_DEC, NULL, 0x0,
599         "", HFILL }},
600
601     { &hf_ddp_dst_node,
602       { "Destination Node",     "ddp.dst.node", FT_UINT8,  BASE_DEC, NULL, 0x0,
603         "", HFILL }},
604
605     { &hf_ddp_src_node,
606       { "Source Node",          "ddp.src.node", FT_UINT8,  BASE_DEC, NULL, 0x0,
607         "", HFILL }},
608
609     { &hf_ddp_dst_socket,
610       { "Destination Socket",   "ddp.dst.socket", FT_UINT8,  BASE_DEC, NULL, 0x0,
611         "", HFILL }},
612
613     { &hf_ddp_src_socket,
614       { "Source Socket",        "ddp.src.socket", FT_UINT8,  BASE_DEC, NULL, 0x0,
615         "", HFILL }},
616
617     { &hf_ddp_type,
618       { "Protocol type",        "ddp.type",     FT_UINT8,  BASE_DEC, VALS(op_vals), 0x0,
619         "", HFILL }},
620   };
621
622   static hf_register_info hf_nbp[] = {
623     { &hf_nbp_op,
624       { "Operation",            "nbp.op",       FT_UINT8,  BASE_DEC, 
625                 VALS(nbp_op_vals), 0xF0, "Operation", HFILL }},
626     { &hf_nbp_info,
627       { "Info",         "nbp.info",     FT_UINT8,  BASE_HEX, 
628                 NULL, 0x0, "Info", HFILL }},
629     { &hf_nbp_count,
630       { "Count",                "nbp.count",    FT_UINT8,  BASE_DEC, 
631                 NULL, 0x0F, "Count", HFILL }},
632     { &hf_nbp_node_net,
633       { "Network",              "nbp.net",      FT_UINT16,  BASE_DEC, 
634                 NULL, 0x0, "Network", HFILL }},
635     { &hf_nbp_node_node,
636       { "Node",         "nbp.node",     FT_UINT8,  BASE_DEC, 
637                 NULL, 0x0, "Node", HFILL }},
638     { &hf_nbp_node_port,
639       { "Port",         "nbp.port",     FT_UINT8,  BASE_DEC, 
640                 NULL, 0x0, "Port", HFILL }},
641     { &hf_nbp_node_enum,
642       { "Enumerator",           "nbp.enum",     FT_UINT8,  BASE_DEC, 
643                 NULL, 0x0, "Enumerator", HFILL }},
644     { &hf_nbp_node_object,
645       { "Object",               "nbp.object",   FT_STRING,  BASE_DEC, 
646                 NULL, 0x0, "Object", HFILL }},
647     { &hf_nbp_node_type,
648       { "Type",         "nbp.type",     FT_STRING,  BASE_DEC, 
649                 NULL, 0x0, "Type", HFILL }},
650     { &hf_nbp_node_zone,
651       { "Zone",         "nbp.zone",     FT_STRING,  BASE_DEC, 
652                 NULL, 0x0, "Zone", HFILL }},
653     { &hf_nbp_tid,
654       { "Transaction ID",               "nbp.tid",      FT_UINT8,  BASE_DEC, 
655                 NULL, 0x0, "Transaction ID", HFILL }}
656   };
657
658   static hf_register_info hf_rtmp[] = {
659     { &hf_rtmp_net,
660       { "Net",          "rtmp.net",     FT_UINT16,  BASE_DEC, 
661                 NULL, 0x0, "Net", HFILL }},
662     { &hf_rtmp_node,
663       { "Node",         "nbp.nodeid",   FT_UINT8,  BASE_DEC, 
664                 NULL, 0x0, "Node", HFILL }},
665     { &hf_rtmp_node_len,
666       { "Node Length",          "nbp.nodeid.length",    FT_UINT8,  BASE_DEC, 
667                 NULL, 0x0, "Node Length", HFILL }},
668     { &hf_rtmp_tuple_net,
669       { "Net",          "rtmp.tuple.net",       FT_UINT16,  BASE_DEC, 
670                 NULL, 0x0, "Net", HFILL }},
671     { &hf_rtmp_tuple_range_start,
672       { "Range Start",          "rtmp.tuple.range_start",       FT_UINT16,  BASE_DEC, 
673                 NULL, 0x0, "Range Start", HFILL }},
674     { &hf_rtmp_tuple_range_end,
675       { "Range End",            "rtmp.tuple.range_end", FT_UINT16,  BASE_DEC, 
676                 NULL, 0x0, "Range End", HFILL }},
677     { &hf_rtmp_tuple_dist,
678       { "Distance",             "rtmp.tuple.dist",      FT_UINT16,  BASE_DEC, 
679                 NULL, 0x0, "Distance", HFILL }},
680     { &hf_rtmp_function,
681       { "Function",             "rtmp.function",        FT_UINT8,  BASE_DEC, 
682                 VALS(rtmp_function_vals), 0x0, "Request Function", HFILL }}
683   };
684
685
686   static gint *ett[] = {
687         &ett_llap,
688         &ett_ddp,
689         &ett_nbp,
690         &ett_nbp_info,
691         &ett_nbp_node,
692         &ett_pstring,
693         &ett_rtmp,
694         &ett_rtmp_tuple
695   };
696
697   proto_llap = proto_register_protocol("LocalTalk Link Access Protocol", "LLAP", "llap");
698   proto_register_field_array(proto_llap, hf_llap, array_length(hf_llap));
699
700   proto_ddp = proto_register_protocol("Datagram Delivery Protocol", "DDP", "ddp");
701   proto_register_field_array(proto_ddp, hf_ddp, array_length(hf_ddp));
702
703   proto_nbp = proto_register_protocol("Name Binding Protocol", "NBP", "nbp");
704   proto_register_field_array(proto_nbp, hf_nbp, array_length(hf_nbp));
705
706   proto_rtmp = proto_register_protocol("Routing Table Maintenance Protocol",
707                                        "RTMP", "rtmp");
708   proto_register_field_array(proto_rtmp, hf_rtmp, array_length(hf_rtmp));
709
710   proto_register_subtree_array(ett, array_length(ett));
711
712   /* subdissector code */
713   ddp_dissector_table = register_dissector_table("ddp.type", "DDP packet type",
714                                                  FT_UINT8, BASE_HEX);
715 }
716
717 void
718 proto_reg_handoff_atalk(void)
719 {
720   dissector_handle_t ddp_handle, nbp_handle, rtmp_request_handle;
721   dissector_handle_t rtmp_data_handle, llap_handle;
722
723   ddp_handle = create_dissector_handle(dissect_ddp, proto_ddp);
724   dissector_add("ethertype", ETHERTYPE_ATALK, ddp_handle);
725   dissector_add("chdlctype", ETHERTYPE_ATALK, ddp_handle);
726   dissector_add("ppp.protocol", PPP_AT, ddp_handle);
727   dissector_add("null.type", BSD_AF_APPLETALK, ddp_handle);
728
729   nbp_handle = create_dissector_handle(dissect_nbp, proto_nbp);
730   dissector_add("ddp.type", DDP_NBP, nbp_handle);
731
732   rtmp_request_handle = create_dissector_handle(dissect_rtmp_request, proto_rtmp);
733   rtmp_data_handle = create_dissector_handle(dissect_rtmp_data, proto_rtmp);
734   dissector_add("ddp.type", DDP_RTMPREQ, rtmp_request_handle);
735   dissector_add("ddp.type", DDP_RTMPDATA, rtmp_data_handle);
736
737   llap_handle = create_dissector_handle(dissect_llap, proto_llap);
738   dissector_add("wtap_encap", WTAP_ENCAP_LOCALTALK, llap_handle);
739
740   data_handle = find_dissector("data");
741 }