Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-nsh.c
blob3107b25fdb79aa22f110e41e35380cdb875b4b1f
1 /* packet-nsh.c
2 * Routines for Network Service Header
4 * RFC8300
5 * Author: Vanson Lim <vlim@cisco.com>
6 * (c) Copyright 2020, Cisco Systems Inc.
8 * draft-ietf-sfc-nsh-01
9 * Author: Chidambaram Arunachalam <carunach@cisco.com>
10 * Copyright 2016, ciscoSystems Inc.
12 * (c) Copyright 2016, Sumit Kumar Jha <sjha3@ncsu.edu>
13 * Support for VXLAN GPE encapsulation
15 * Wireshark - Network traffic analyzer
16 * By Gerald Combs <gerald@wireshark.org>
17 * Copyright 1998 Gerald Combs
19 * SPDX-License-Identifier: GPL-2.0-or-later
22 #include "config.h"
23 #include <epan/packet.h>
24 #include <epan/etypes.h>
25 #include <epan/expert.h>
26 #include <epan/ipproto.h>
27 #include "packet-nsh.h"
28 #include "packet-vxlan.h"
30 #define MD_TYPE_1 1
31 #define MD_TYPE_2 2
33 #define MD_MAX_VERSION 0
35 /* Prototypes */
36 void proto_reg_handoff_nsh(void);
37 void proto_register_nsh(void);
39 static dissector_handle_t nsh_handle;
41 static const value_string nsh_next_protocols[] = {
42 { NSH_NONE, "None" },
43 { NSH_IPV4, "IPv4" },
44 { NSH_IPV6, "IPv6" },
45 { NSH_ETHERNET, "Ethernet" },
46 { NSH_NSH, "NSH" },
47 { NSH_MPLS, "MPLS" },
48 { NSH_EXPERIMENT_1, "Experiment 1" },
49 { NSH_EXPERIMENT_2, "Experiment 2" },
50 { 0, NULL }
54 static int proto_nsh;
55 static int hf_nsh_version;
56 static int hf_nsh_oam;
57 static int hf_nsh_critical_metadata;
58 static int hf_nsh_ttl;
59 static int hf_nsh_length;
60 static int hf_nsh_md_type;
61 static int hf_nsh_next_proto;
62 static int hf_nsh_service_pathID;
63 static int hf_nsh_service_index;
64 static int hf_nsh_context_header;
65 static int hf_nsh_metadata_class;
66 static int hf_nsh_metadata_type;
67 static int hf_nsh_metadata_length;
68 static int hf_nsh_metadata;
69 static int hf_nsh_bbf_logical_port_id;
70 static int hf_nsh_bbf_logical_port_id_str;
71 static int hf_nsh_bbf_mac;
72 static int hf_nsh_bbf_network_instance;
73 static int hf_nsh_bbf_interface_id;
75 static expert_field ei_nsh_length_invalid;
76 static expert_field ei_nsh_tlv_incomplete_dissection;
78 static int ett_nsh;
79 static int ett_nsh_tlv;
81 static dissector_table_t subdissector_table;
82 static dissector_table_t tlv_table;
85 *Dissect Fixed Length Context headers
88 static void
89 dissect_nsh_md_type_1(tvbuff_t *tvb, proto_tree *nsh_tree, int offset)
91 proto_tree_add_item(nsh_tree, hf_nsh_context_header, tvb, offset, 4, ENC_NA);
92 proto_tree_add_item(nsh_tree, hf_nsh_context_header, tvb, offset + 4, 4, ENC_NA);
93 proto_tree_add_item(nsh_tree, hf_nsh_context_header, tvb, offset + 8, 4, ENC_NA);
94 proto_tree_add_item(nsh_tree, hf_nsh_context_header, tvb, offset + 12, 4, ENC_NA);
98 *Dissect Variable Length Context headers
102 static void
103 dissect_nsh_md_type_2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *nsh_tree, int offset, int nsh_bytes_len)
105 while (offset < nsh_bytes_len) {
106 uint16_t tlv_class = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN);
107 uint8_t tlv_type = tvb_get_uint8(tvb, offset + 2);
108 uint8_t tlv_len = tvb_get_uint8(tvb, offset + 3) & 0x7F;
110 proto_item *tlv_item;
111 proto_tree *tlv_tree = proto_tree_add_subtree_format(nsh_tree, tvb, offset, 4 + tlv_len, ett_nsh_tlv, &tlv_item, "TLV: Class %u Type %u", tlv_class, tlv_type);
113 proto_tree_add_item(tlv_tree, hf_nsh_metadata_class, tvb, offset, 2, ENC_BIG_ENDIAN);
114 proto_tree_add_item(tlv_tree, hf_nsh_metadata_type, tvb, offset + 2, 1, ENC_BIG_ENDIAN);
115 proto_tree_add_item(tlv_tree, hf_nsh_metadata_length, tvb, offset + 3, 1, ENC_BIG_ENDIAN);
116 offset += 4;
118 if (tlv_len > 0)
120 tvbuff_t *tlv_tvb = tvb_new_subset_length(tvb, offset, tlv_len);
121 const uint32_t key = ((uint32_t) tlv_class << 8) | tlv_type;
122 int dissected = dissector_try_uint(tlv_table, key, tlv_tvb, pinfo, tlv_tree);
124 if (dissected == 0) {
125 proto_tree_add_item(tlv_tree, hf_nsh_metadata, tlv_tvb, 0, -1, ENC_NA);
126 } else if (dissected > 0 && (unsigned) dissected != tlv_len) {
127 expert_add_info_format(pinfo, tlv_tree, &ei_nsh_tlv_incomplete_dissection, "TLV dissector did not dissect the whole data (%d != %d)", dissected, tlv_len);
130 offset += ((tlv_len + 3) / 4) * 4; // aligned up on 4-byte boundary
137 *Dissect Network Service Header
141 static int
142 dissect_nsh(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
145 int offset = 0;
146 int md_type = -1;
147 uint32_t nsh_bytes_len;
148 int nsh_next_proto = -1;
149 proto_item *length_pi;
150 tvbuff_t *next_tvb;
152 col_set_str(pinfo->cinfo, COL_PROTOCOL, "NSH");
153 col_set_str(pinfo->cinfo, COL_INFO, "Network Service Header");
155 proto_item *ti;
156 proto_tree *nsh_tree;
158 ti = proto_tree_add_item(tree, proto_nsh, tvb, offset, 2, ENC_NA);
159 nsh_tree = proto_item_add_subtree(ti, ett_nsh);
161 /*NSH Base Header*/
163 proto_tree_add_item(nsh_tree, hf_nsh_version, tvb, offset, 2, ENC_BIG_ENDIAN);
164 proto_tree_add_item(nsh_tree, hf_nsh_oam, tvb, offset, 2, ENC_BIG_ENDIAN);
165 proto_tree_add_item(nsh_tree, hf_nsh_critical_metadata, tvb, offset, 2, ENC_BIG_ENDIAN);
168 /*NSH Time to live Bits 4 - 9*/
169 proto_tree_add_item(nsh_tree, hf_nsh_ttl, tvb, offset, 2, ENC_BIG_ENDIAN);
170 length_pi = proto_tree_add_item_ret_uint(nsh_tree, hf_nsh_length, tvb, offset, 2, ENC_BIG_ENDIAN, &nsh_bytes_len);
171 nsh_bytes_len *= 4;
172 proto_item_set_len(ti, nsh_bytes_len);
175 md_type = tvb_get_uint8(tvb, offset + 2);
176 proto_tree_add_item(nsh_tree, hf_nsh_md_type, tvb, offset + 2, 1, ENC_BIG_ENDIAN);
178 nsh_next_proto = tvb_get_uint8(tvb, offset + 3);
179 proto_tree_add_item(nsh_tree, hf_nsh_next_proto, tvb, offset + 3, 1, ENC_BIG_ENDIAN);
181 /*NSH Service Path Header */
182 offset = offset + 4;
183 proto_tree_add_item(nsh_tree, hf_nsh_service_pathID, tvb, offset, 3, ENC_BIG_ENDIAN);
184 proto_tree_add_item(nsh_tree, hf_nsh_service_index, tvb, offset + 3, 1, ENC_BIG_ENDIAN);
186 /* Decode Context Headers */
187 offset = offset + 4;
188 switch (md_type) {
190 case MD_TYPE_1:
191 /* The Length MUST be of value 0x6 for MD Type equal to 0x1 */
192 if (nsh_bytes_len != 4 * 6) {
193 expert_add_info_format(pinfo, length_pi, &ei_nsh_length_invalid,
194 "Length MUST be of value 0x6 for MD Type equal to 0x1");
195 nsh_bytes_len = 4 * 6;
197 dissect_nsh_md_type_1(tvb, nsh_tree, offset);
198 break;
200 case MD_TYPE_2:
202 /* The Length MUST be of value 0x2 or greater for MD Type equal to 0x2 */
203 if (nsh_bytes_len < 4 * 2) {
204 expert_add_info_format(pinfo, length_pi, &ei_nsh_length_invalid,
205 "Length MUST be of value 0x2 or greater for MD Type equal to 0x2");
206 nsh_bytes_len = 4 * 2;
208 /* MD Type 2 indicates ZERO or more Variable Length Context headers*/
209 if (nsh_bytes_len > 8)
210 dissect_nsh_md_type_2(tvb, pinfo, nsh_tree, offset, nsh_bytes_len);
211 break;
213 default:
215 * Unknown type, but assume presence of at least the NSH
216 * Base Header (32 bits, 4 bytes).
218 if (nsh_bytes_len < 4) {
219 expert_add_info_format(pinfo, length_pi, &ei_nsh_length_invalid,
220 "Length must be at least 0x1 for NSH Base Header");
221 nsh_bytes_len = 4;
223 break;
227 /*Decode next protocol payload */
229 if (tvb_captured_length_remaining(tvb, nsh_bytes_len) > 0) {
230 next_tvb = tvb_new_subset_remaining(tvb, nsh_bytes_len);
231 if (!dissector_try_uint(subdissector_table, nsh_next_proto, next_tvb, pinfo, tree)) {
232 call_data_dissector(next_tvb, pinfo, tree);
236 return tvb_captured_length(tvb);
240 typedef struct {
241 uint16_t class;
242 uint8_t type;
243 const char* name;
244 dissector_t dissector;
245 } nsh_tlv;
247 static int dissect_tlv_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data, void *cb_data)
249 const nsh_tlv* tlv = cb_data;
250 proto_item_set_text(proto_tree_get_parent(tree), "TLV: %s", tlv->name);
251 return tlv->dissector(tvb, pinfo, tree, data);
254 static int dissect_tlv_logical_port(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
256 if (tvb_ascii_isprint(tvb, 0, -1))
258 const uint8_t* string_value;
259 proto_tree_add_item_ret_string(tree, hf_nsh_bbf_logical_port_id_str, tvb, 0, -1, ENC_ASCII | ENC_NA, pinfo->pool, &string_value);
260 proto_item_append_text(proto_tree_get_parent(tree), ": %s", string_value);
262 else
264 proto_tree_add_item(tree, hf_nsh_bbf_logical_port_id, tvb, 0, -1, ENC_NA);
267 return tvb_reported_length(tvb);
270 static int dissect_tlv_mac(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
272 proto_tree_add_item(tree, hf_nsh_bbf_mac, tvb, 0, 6, ENC_NA);
273 return 6;
276 static int dissect_tlv_network_instance(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
278 const uint8_t* string_value;
279 proto_tree_add_item_ret_string(tree, hf_nsh_bbf_network_instance, tvb, 0, -1, ENC_ASCII | ENC_NA, pinfo->pool, &string_value);
280 proto_item_append_text(proto_tree_get_parent(tree), ": %s", string_value);
281 return tvb_reported_length(tvb);
284 static int dissect_tlv_iface_identifier(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data _U_)
286 proto_tree_add_item(tree, hf_nsh_bbf_interface_id, tvb, 0, 8, ENC_NA);
287 return 8;
290 static void register_tlv_dissectors(void)
292 /* The TLV subdissector table contains all dissectors for the TLV data.
293 The key for the dissector is a combination of class + type.
294 In order to be able to use a dissector-table easily, these 2 bytes are combined into a
295 24-bit integer, containing the concatenation of class and type (as they appear on the wire in network-order).
297 Relevant RFC section: https://datatracker.ietf.org/doc/html/rfc8300#section-9.1.4
299 static const nsh_tlv tlvs[] = {
300 // TLVs defined by BBF in TR-459i2:
301 {0x0200, 0x00, "Logical Port", dissect_tlv_logical_port},
302 {0x0200, 0x01, "MAC", dissect_tlv_mac},
303 {0x0200, 0x02, "Network Instance", dissect_tlv_network_instance},
304 {0x0200, 0x03, "Interface Identifier", dissect_tlv_iface_identifier},
307 for (unsigned i = 0; i < array_length(tlvs); i++) {
308 const uint32_t key = ((uint32_t) tlvs[i].class << 8) | tlvs[i].type;
309 dissector_add_uint("nsh.tlv", key, create_dissector_handle_with_data(dissect_tlv_data, -1, (void*) &tlvs[i]));
313 static bool
314 dissect_nsh_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
316 const int tvb_length = tvb_captured_length(tvb);
317 if (tvb_length < 8) return false;
319 const uint8_t version = tvb_get_uint8(tvb, 0) >> 6;
320 const uint8_t length = tvb_get_uint8(tvb, 1) & 0x3F;
321 const uint8_t md_type = tvb_get_uint8(tvb, 2) & 0x0F;
322 const uint8_t proto = tvb_get_uint8(tvb, 3);
324 if (version > MD_MAX_VERSION) return false;
325 if (md_type != 1 && md_type != 2) return false;
326 if (md_type == 1 && length != 6) return false;
327 if (md_type == 2 && length < 2) return false;
328 if (length * 4 > tvb_length) return false;
329 if (proto == 0) return false;
330 if (proto > NSH_MAX_PROTOCOL) return false;
332 // Note: md_type = 0x0 and md_type = 0xf are strictly speaking also valid.
333 // For the heuristic to work as good as possible, it is best to restrict
334 // as much as possible and only allow md_type 1 and 2.
336 dissect_nsh(tvb, pinfo, tree, data);
337 return true;
340 void
341 proto_register_nsh(void)
343 expert_module_t *expert_nsh;
345 static hf_register_info nsh_info[] = {
347 /* Network Service Header fields */
348 { &hf_nsh_version,
349 { "Version", "nsh.version",
350 FT_UINT16, BASE_DEC_HEX, NULL, 0xC000,
351 NULL, HFILL }
354 { &hf_nsh_oam,
355 { "O Bit", "nsh.Obit",
356 FT_UINT16, BASE_DEC, NULL, 0x2000,
357 "OAM Bit", HFILL }
361 { &hf_nsh_critical_metadata,
362 { "C Bit", "nsh.CBit",
363 FT_UINT16, BASE_DEC, NULL, 0x1000,
364 "Critical Metadata Bit", HFILL }
368 { &hf_nsh_ttl,
369 { "Time to live", "nsh.ttl",
370 FT_UINT16, BASE_HEX, NULL, 0x0FC0,
371 "Maximum SFF hops for an SFP, this field is used for service-plane loop detection", HFILL }
375 { &hf_nsh_length,
376 { "Length", "nsh.length",
377 FT_UINT16, BASE_DEC_HEX, NULL, 0x003F,
378 "Total length, in 4-byte words, of NSH including Base, Service Path headers and optional variable TLVs", HFILL }
382 { &hf_nsh_md_type,
383 { "MD Type", "nsh.mdtype",
384 FT_UINT8, BASE_DEC_HEX, NULL, 0x00,
385 "Metadata Type defines the format of the metadata being carried", HFILL }
389 { &hf_nsh_next_proto,
390 { "Next Protocol", "nsh.nextproto",
391 FT_UINT8, BASE_DEC_HEX, VALS(nsh_next_protocols), 0x00,
392 "Protocol type of the original packet", HFILL }
396 { &hf_nsh_service_pathID,
397 { "SPI", "nsh.spi",
398 FT_UINT24, BASE_DEC_HEX, NULL, 0x00,
399 "Service Path Identifier", HFILL }
403 { &hf_nsh_service_index,
404 { "SI", "nsh.si",
405 FT_UINT8, BASE_DEC_HEX, NULL, 0x00,
406 "Service Index", HFILL }
411 { &hf_nsh_context_header,
412 { "Context Header", "nsh.contextheader",
413 FT_BYTES, BASE_NONE, NULL, 0x00,
414 "Mandatory Context Header", HFILL }
418 { &hf_nsh_metadata_class,
419 { "TLV Class", "nsh.metadataclass",
420 FT_UINT16, BASE_DEC_HEX, NULL, 0x00,
421 "TLV class describes the scope of the metadata type field", HFILL }
425 { &hf_nsh_metadata_type,
426 { "Type", "nsh.metadatatype",
427 FT_UINT8, BASE_DEC_HEX, NULL, 0x00,
428 "Type of metadata", HFILL }
432 { &hf_nsh_metadata_length,
433 { "Length", "nsh.metadatalen",
434 FT_UINT8, BASE_HEX, NULL, 0x7F,
435 "Length of the variable metadata in bytes", HFILL }
439 { &hf_nsh_metadata,
440 { "Variable Metadata", "nsh.metadata",
441 FT_BYTES, BASE_NONE, NULL, 0x00,
442 "Variable length metadata", HFILL }
445 { &hf_nsh_bbf_logical_port_id,
446 { "Logical Port", "nsh.tlv.bbf.logical_port_id",
447 FT_BYTES, BASE_NONE, NULL, 0x0,
448 NULL, HFILL }
450 { &hf_nsh_bbf_logical_port_id_str,
451 { "Logical Port", "nsh.tlv.bbf.logical_port_id_str",
452 FT_STRING, BASE_NONE, NULL, 0x0,
453 NULL, HFILL }
455 { &hf_nsh_bbf_mac,
456 { "MAC Address", "nsh.tlv.bbf.mac",
457 FT_ETHER, BASE_NONE, NULL, 0x0,
458 NULL, HFILL }
460 { &hf_nsh_bbf_network_instance,
461 { "Network Instance", "nsh.tlv.bbf.network_instance",
462 FT_STRING, BASE_NONE, NULL, 0x0,
463 NULL, HFILL }
465 { &hf_nsh_bbf_interface_id,
466 { "Interface Identifier", "nsh.tlv.bbf.interface_id",
467 FT_BYTES, BASE_NONE, NULL, 0x0,
468 NULL, HFILL }
474 static int *ett[] = {
475 &ett_nsh,
476 &ett_nsh_tlv,
479 static ei_register_info ei[] = {
480 { &ei_nsh_length_invalid, { "nsh.length.invalid", PI_PROTOCOL, PI_WARN, "Invalid total length", EXPFILL }},
481 { &ei_nsh_tlv_incomplete_dissection, { "nsh.tlv.incomplete", PI_PROTOCOL, PI_WARN, "Incomplete TLV dissection", EXPFILL }},
484 proto_nsh = proto_register_protocol("Network Service Header", "NSH", "nsh");
485 proto_register_field_array(proto_nsh, nsh_info, array_length(nsh_info));
486 proto_register_subtree_array(ett, array_length(ett));
488 expert_nsh = expert_register_protocol(proto_nsh);
489 expert_register_field_array(expert_nsh, ei, array_length(ei));
491 subdissector_table = register_dissector_table("nsh.next_proto", "NSH Next Protocol", proto_nsh, FT_UINT32, BASE_DEC);
492 tlv_table = register_dissector_table("nsh.tlv", "NSH TLV", proto_nsh, FT_UINT24, BASE_HEX);
494 register_tlv_dissectors();
496 nsh_handle = register_dissector("nsh", dissect_nsh, proto_nsh);
499 void
500 proto_reg_handoff_nsh(void)
502 dissector_add_uint("ethertype", ETHERTYPE_NSH, nsh_handle);
503 dissector_add_uint("gre.proto", ETHERTYPE_NSH, nsh_handle);
504 dissector_add_uint("vxlan.next_proto", VXLAN_NSH, nsh_handle);
505 dissector_add_uint("nsh.next_proto", NSH_NSH, nsh_handle);
506 dissector_add_uint("ip.proto", IP_PROTO_NSH, nsh_handle);
508 heur_dissector_add("gtp.tpdu", dissect_nsh_heur, "NSH over GTP", "nsh_gtp.tpdu", proto_nsh, HEURISTIC_ENABLE);
512 * Editor modelines - https://www.wireshark.org/tools/modelines.html
514 * Local variables:
515 * c-basic-offset: 4
516 * tab-width: 8
517 * indent-tabs-mode: nil
518 * End:
520 * vi: set shiftwidth=4 tabstop=8 expandtab:
521 * :indentSize=4:tabSize=8:noTabs=true: