3 * Routines for Amateur Packet Radio protocol dissection
4 * NET/ROM inter-node frames.
5 * Copyright 2005,2006,2007,2008,2009,2010,2012 R.W. Stearn <richard@rns-stearn.demon.co.uk>
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 * Information on the protocol drawn from:
31 * Protocol specification is at:
33 * ftp://ftp.ucsd.edu/hamradio/packet/tcpip/docs/netrom.ps.gz
35 * (yes, it's PostScript, and, yes, it's an FTP URL).
37 * Inspiration on how to build the dissector drawn from
43 * with the base file built from README.developers.
50 #include <epan/packet.h>
51 #include <epan/to_str.h>
52 #include <epan/wmem/wmem.h>
53 #include <epan/ax25_pids.h>
55 #include "packet-netrom.h"
59 #define AX25_ADDR_LEN 7 /* length of an AX.25 address */
61 #define NETROM_MIN_SIZE 7 /* minumum payload for a routing packet */
62 #define NETROM_HEADER_SIZE 20 /* minumum payload for a normal packet */
64 #define NETROM_PROTOEXT 0x00
65 #define NETROM_CONNREQ 0x01
66 #define NETROM_CONNACK 0x02
67 #define NETROM_DISCREQ 0x03
68 #define NETROM_DISCACK 0x04
69 #define NETROM_INFO 0x05
70 #define NETROM_INFOACK 0x06
72 #define NETROM_MORE_FLAG 0x20
73 #define NETROM_NAK_FLAG 0x40
74 #define NETROM_CHOKE_FLAG 0x80
76 #define NETROM_PROTO_IP 0x0C
78 /* Forward declaration we need below */
79 void proto_reg_handoff_netrom(void);
81 /* Dissector handles - all the possibles are listed */
82 static dissector_handle_t ip_handle
;
83 static dissector_handle_t default_handle
;
85 /* Initialize the protocol and registered fields */
86 static int proto_netrom
= -1;
87 static int hf_netrom_src
= -1;
88 static int hf_netrom_dst
= -1;
89 static int hf_netrom_ttl
= -1;
90 static int hf_netrom_my_cct_index
= -1;
91 static int hf_netrom_my_cct_id
= -1;
92 static int hf_netrom_your_cct_index
= -1;
93 static int hf_netrom_your_cct_id
= -1;
94 static int hf_netrom_n_r
= -1;
95 static int hf_netrom_n_s
= -1;
96 static int hf_netrom_type
= -1;
97 static int hf_netrom_op
= -1;
98 static int hf_netrom_more
= -1;
99 static int hf_netrom_nak
= -1;
100 static int hf_netrom_choke
= -1;
102 static int hf_netrom_user
= -1;
103 static int hf_netrom_node
= -1;
104 static int hf_netrom_pwindow
= -1;
105 static int hf_netrom_awindow
= -1;
107 static int hf_netrom_mnemonic
= -1;
110 * Structure containing pointers to hf_ values for various subfields of
120 static const netrom_tf_items netrom_type_items
= {
128 const value_string op_code_vals_abbrev
[] = {
129 { NETROM_PROTOEXT
, "PROTOEXT"},
130 { NETROM_CONNREQ
, "CONNREQ"},
131 { NETROM_CONNACK
, "CONNACK"},
132 { NETROM_DISCREQ
, "DISCREQ"},
133 { NETROM_DISCACK
, "DISCACK"},
134 { NETROM_INFO
, "INFO"},
135 { NETROM_INFOACK
, "INFOACK"},
139 const value_string op_code_vals_text
[] = {
140 { NETROM_PROTOEXT
, "Protocol extension"},
141 { NETROM_CONNREQ
, "Connect request"},
142 { NETROM_CONNACK
, "Connect acknowledge"},
143 { NETROM_DISCREQ
, "Disconnect request"},
144 { NETROM_DISCACK
, "Disconnect acknowledge"},
145 { NETROM_INFO
, "Information"},
146 { NETROM_INFOACK
, "Information acknowledge"},
150 /* Initialize the subtree pointers */
151 static gint ett_netrom
= -1;
152 static gint ett_netrom_type
= -1;
155 dissect_netrom_type(tvbuff_t
*tvb
, int offset
, packet_info
*pinfo
, proto_tree
*tree
,
156 int hf_netrom_type_param
, gint ett_netrom_type_param
, const netrom_tf_items
*type_items
)
159 proto_tree
*type_tree
;
164 type
= tvb_get_guint8( tvb
, offset
);
165 op_code
= type
&0x0f;
167 info_buffer
= wmem_strdup_printf( wmem_packet_scope(), "%s%s%s%s (0x%02x)",
168 val_to_str_const( op_code
, op_code_vals_text
, "Unknown" ),
169 ( type
& NETROM_MORE_FLAG
) ? ", More" : "",
170 ( type
& NETROM_NAK_FLAG
) ? ", NAK" : "",
171 ( type
& NETROM_CHOKE_FLAG
) ? ", Choke" : "",
173 col_add_str( pinfo
->cinfo
, COL_INFO
, info_buffer
);
177 tc
= proto_tree_add_uint_format( tree
,
178 hf_netrom_type_param
,
186 type_tree
= proto_item_add_subtree( tc
, ett_netrom_type_param
);
188 proto_tree_add_item( type_tree
, *type_items
->hf_tf_op
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
189 proto_tree_add_item( type_tree
, *type_items
->hf_tf_choke
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
190 proto_tree_add_item( type_tree
, *type_items
->hf_tf_nak
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
191 proto_tree_add_item( type_tree
, *type_items
->hf_tf_more
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
196 dissect_netrom_proto(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
199 proto_tree
*netrom_tree
;
201 const guint8
*src_addr
;
202 const guint8
*dst_addr
;
203 const guint8
*user_addr
;
204 const guint8
*node_addr
;
212 tvbuff_t
*next_tvb
= NULL
;
214 col_set_str( pinfo
->cinfo
, COL_PROTOCOL
, "NET/ROM" );
215 col_clear( pinfo
->cinfo
, COL_INFO
);
220 src_addr
= tvb_get_ptr( tvb
, offset
, AX25_ADDR_LEN
);
221 SET_ADDRESS(&pinfo
->dl_src
, AT_AX25
, AX25_ADDR_LEN
, src_addr
);
222 SET_ADDRESS(&pinfo
->src
, AT_AX25
, AX25_ADDR_LEN
, src_addr
);
223 /* src_ssid = *(src_addr + 6); */
224 offset
+= AX25_ADDR_LEN
; /* step over src addr */
227 dst_addr
= tvb_get_ptr( tvb
, offset
, AX25_ADDR_LEN
);
228 SET_ADDRESS(&pinfo
->dl_dst
, AT_AX25
, AX25_ADDR_LEN
, dst_addr
);
229 SET_ADDRESS(&pinfo
->dst
, AT_AX25
, AX25_ADDR_LEN
, dst_addr
);
230 /* dst_ssid = *(dst_addr + 6); */
231 offset
+= AX25_ADDR_LEN
; /* step over dst addr */
233 offset
+= 1; /* step over ttl */
234 cct_index
= tvb_get_guint8( tvb
, offset
);
235 offset
+= 1; /* step over cct index*/
236 cct_id
= tvb_get_guint8( tvb
, offset
);
237 offset
+= 1; /* step over cct id */
238 offset
+= 1; /* step over n_s */
239 offset
+= 1; /* step over n_r */
242 op_code
= tvb_get_guint8( tvb
, offset
) & 0x0f;
243 offset
+= 1; /* step over op_code */
245 col_add_fstr( pinfo
->cinfo
, COL_INFO
, "%s", val_to_str_const( op_code
, op_code_vals_text
, "Unknown" ));
249 /* create display subtree for the protocol */
251 ti
= proto_tree_add_protocol_format( tree
, proto_netrom
, tvb
, 0, NETROM_HEADER_SIZE
,
252 "NET/ROM, Src: %s (%s), Dst: %s (%s)",
253 get_ax25_name( src_addr
),
254 ax25_to_str( src_addr
),
255 get_ax25_name( dst_addr
),
256 ax25_to_str( dst_addr
) );
258 netrom_tree
= proto_item_add_subtree( ti
, ett_netrom
);
263 proto_tree_add_ax25( netrom_tree
, hf_netrom_src
, tvb
, offset
, AX25_ADDR_LEN
, src_addr
);
264 offset
+= AX25_ADDR_LEN
;
267 proto_tree_add_ax25( netrom_tree
, hf_netrom_dst
, tvb
, offset
, AX25_ADDR_LEN
, dst_addr
);
268 offset
+= AX25_ADDR_LEN
;
271 proto_tree_add_item( netrom_tree
, hf_netrom_ttl
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
276 case NETROM_PROTOEXT
:
278 proto_tree_add_item( netrom_tree
, hf_netrom_my_cct_index
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
282 proto_tree_add_item( netrom_tree
, hf_netrom_my_cct_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
291 case NETROM_CONNREQ
:
293 proto_tree_add_item( netrom_tree
, hf_netrom_my_cct_index
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
297 proto_tree_add_item( netrom_tree
, hf_netrom_my_cct_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
307 case NETROM_CONNACK
:
309 proto_tree_add_item( netrom_tree
, hf_netrom_your_cct_index
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
313 proto_tree_add_item( netrom_tree
, hf_netrom_your_cct_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
317 proto_tree_add_item( netrom_tree
, hf_netrom_my_cct_index
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
321 proto_tree_add_item( netrom_tree
, hf_netrom_my_cct_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
325 case NETROM_DISCREQ
:
327 proto_tree_add_item( netrom_tree
, hf_netrom_your_cct_index
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
331 proto_tree_add_item( netrom_tree
, hf_netrom_your_cct_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
341 case NETROM_DISCACK
:
343 proto_tree_add_item( netrom_tree
, hf_netrom_your_cct_index
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
347 proto_tree_add_item( netrom_tree
, hf_netrom_your_cct_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
359 proto_tree_add_item( netrom_tree
, hf_netrom_your_cct_index
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
363 proto_tree_add_item( netrom_tree
, hf_netrom_your_cct_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
367 proto_tree_add_item( netrom_tree
, hf_netrom_n_s
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
371 proto_tree_add_item( netrom_tree
, hf_netrom_n_r
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
375 case NETROM_INFOACK
:
377 proto_tree_add_item( netrom_tree
, hf_netrom_your_cct_index
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
381 proto_tree_add_item( netrom_tree
, hf_netrom_your_cct_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
388 proto_tree_add_item( netrom_tree
, hf_netrom_n_r
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
402 dissect_netrom_type( tvb
,
414 case NETROM_PROTOEXT
:
416 case NETROM_CONNREQ
:
417 /* proposed window size */
418 proto_tree_add_item( netrom_tree
, hf_netrom_pwindow
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
421 user_addr
= tvb_get_ptr( tvb
, offset
, AX25_ADDR_LEN
);
422 proto_tree_add_ax25( netrom_tree
, hf_netrom_user
, tvb
, offset
, AX25_ADDR_LEN
, user_addr
);
423 offset
+= AX25_ADDR_LEN
;
425 node_addr
= tvb_get_ptr( tvb
, offset
, AX25_ADDR_LEN
);
426 proto_tree_add_ax25( netrom_tree
, hf_netrom_node
, tvb
, offset
, AX25_ADDR_LEN
, node_addr
);
427 offset
+= AX25_ADDR_LEN
;
430 case NETROM_CONNACK
:
431 /* accepted window size */
432 proto_tree_add_item( netrom_tree
, hf_netrom_awindow
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
436 case NETROM_DISCREQ
:
438 case NETROM_DISCACK
:
442 case NETROM_INFOACK
:
449 /* Call sub-dissectors here */
451 next_tvb
= tvb_new_subset_remaining(tvb
, offset
);
455 case NETROM_PROTOEXT
:
456 if ( cct_index
== NETROM_PROTO_IP
&& cct_id
== NETROM_PROTO_IP
)
457 call_dissector( ip_handle
, next_tvb
, pinfo
, tree
);
459 call_dissector( default_handle
, next_tvb
, pinfo
, tree
);
464 call_dissector( default_handle
, next_tvb
, pinfo
, tree
);
470 dissect_netrom_routing(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
474 col_set_str( pinfo
->cinfo
, COL_PROTOCOL
, "NET/ROM");
475 col_set_str( pinfo
->cinfo
, COL_INFO
, "routing table frame");
480 proto_tree
*netrom_tree
;
481 ti
= proto_tree_add_protocol_format( tree
, proto_netrom
, tvb
, 0, -1,
482 "NET/ROM, routing table frame, Node: %.6s",
483 tvb_get_ptr( tvb
, 1, 6 )
486 netrom_tree
= proto_item_add_subtree( ti
, ett_netrom
);
488 proto_tree_add_item( netrom_tree
, hf_netrom_mnemonic
, tvb
, 1, 6, ENC_ASCII
|ENC_NA
);
491 next_tvb
= tvb_new_subset_remaining(tvb
, 7);
493 call_dissector( default_handle
, next_tvb
, pinfo
, tree
);
496 /* Code to actually dissect the packets */
498 dissect_netrom(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
500 if ( tvb_get_guint8( tvb
, 0 ) == 0xff )
501 dissect_netrom_routing( tvb
, pinfo
, tree
);
503 dissect_netrom_proto( tvb
, pinfo
, tree
);
507 capture_netrom( const guchar
*pd _U_
, int offset
, int len
, packet_counts
*ld
)
509 if ( ! BYTES_ARE_IN_FRAME( offset
, len
, NETROM_MIN_SIZE
) )
514 /* XXX - check for IP-over-NetROM here! */
519 proto_register_netrom(void)
521 static const true_false_string flags_set_truth
=
528 /* Setup list of header fields */
529 static hf_register_info hf
[] = {
531 { "Source", "netrom.src",
532 FT_AX25
, BASE_NONE
, NULL
, 0x0,
533 "Source callsign", HFILL
}
536 { "Destination", "netrom.dst",
537 FT_AX25
, BASE_NONE
, NULL
, 0x0,
538 "Destination callsign", HFILL
}
541 { "TTL", "netrom.ttl",
542 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
545 { &hf_netrom_my_cct_index
,
546 { "My circuit index", "netrom.my.cct.index",
547 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
550 { &hf_netrom_my_cct_id
,
551 { "My circuit ID", "netrom.my.cct.id",
552 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
555 { &hf_netrom_your_cct_index
,
556 { "Your circuit index", "netrom.your.cct.index",
557 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
560 { &hf_netrom_your_cct_id
,
561 { "Your circuit ID", "netrom.your.cct.id",
562 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
566 { "N(r)", "netrom.n_r",
567 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
571 { "N(s)", "netrom.n_s",
572 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
576 { "Type", "netrom.type",
577 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
578 "Packet type field", HFILL
}
581 { "OP code", "netrom.op",
582 FT_UINT8
, BASE_HEX
, VALS( op_code_vals_abbrev
), 0x0f,
583 "Protocol operation code", HFILL
}
586 { "More", "netrom.flag.more",
587 FT_BOOLEAN
, 8, TFS(&flags_set_truth
), NETROM_MORE_FLAG
,
591 { "NAK", "netrom.flag.nak",
592 FT_BOOLEAN
, 8, TFS(&flags_set_truth
), NETROM_NAK_FLAG
,
596 { "Choke", "netrom.flag.choke",
597 FT_BOOLEAN
, 8, TFS(&flags_set_truth
), NETROM_CHOKE_FLAG
,
598 "Choke flag", HFILL
}
601 { "User", "netrom.user",
602 FT_AX25
, BASE_NONE
, NULL
, 0x0,
603 "User callsign", HFILL
}
606 { "Node", "netrom.node",
607 FT_AX25
, BASE_NONE
, NULL
, 0x0,
608 "Node callsign", HFILL
}
610 { &hf_netrom_pwindow
,
611 { "Window", "netrom.pwindow",
612 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
613 "Proposed window", HFILL
}
615 { &hf_netrom_awindow
,
616 { "Window", "netrom.awindow",
617 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
618 "Accepted window", HFILL
}
620 { &hf_netrom_mnemonic
,
621 { "Node name", "netrom.name",
622 FT_STRING
, BASE_NONE
, NULL
, 0x0,
627 /* Setup protocol subtree array */
628 static gint
*ett
[] = {
633 /* Register the protocol name and description */
634 proto_netrom
= proto_register_protocol( "Amateur Radio NET/ROM", "NET/ROM", "netrom" );
636 /* Required function calls to register the header fields and subtrees used */
637 proto_register_field_array( proto_netrom
, hf
, array_length(hf
) );
638 proto_register_subtree_array( ett
, array_length( ett
) );
642 proto_reg_handoff_netrom(void)
644 dissector_add_uint( "ax25.pid", AX25_P_NETROM
, create_dissector_handle( dissect_netrom
, proto_netrom
) );
646 ip_handle
= find_dissector( "ip" );
647 default_handle
= find_dissector( "data" );