2 * Routines for Network Service Header
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
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"
33 #define MD_MAX_VERSION 0
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
[] = {
45 { NSH_ETHERNET
, "Ethernet" },
48 { NSH_EXPERIMENT_1
, "Experiment 1" },
49 { NSH_EXPERIMENT_2
, "Experiment 2" },
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
;
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
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
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
);
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
142 dissect_nsh(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
147 uint32_t nsh_bytes_len
;
148 int nsh_next_proto
= -1;
149 proto_item
*length_pi
;
152 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "NSH");
153 col_set_str(pinfo
->cinfo
, COL_INFO
, "Network Service Header");
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
);
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
);
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 */
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 */
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
);
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
);
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");
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
);
244 dissector_t dissector
;
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
);
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
);
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
);
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
]));
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
);
341 proto_register_nsh(void)
343 expert_module_t
*expert_nsh
;
345 static hf_register_info nsh_info
[] = {
347 /* Network Service Header fields */
349 { "Version", "nsh.version",
350 FT_UINT16
, BASE_DEC_HEX
, NULL
, 0xC000,
355 { "O Bit", "nsh.Obit",
356 FT_UINT16
, BASE_DEC
, NULL
, 0x2000,
361 { &hf_nsh_critical_metadata
,
362 { "C Bit", "nsh.CBit",
363 FT_UINT16
, BASE_DEC
, NULL
, 0x1000,
364 "Critical Metadata Bit", HFILL
}
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
}
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
}
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
,
398 FT_UINT24
, BASE_DEC_HEX
, NULL
, 0x00,
399 "Service Path Identifier", HFILL
}
403 { &hf_nsh_service_index
,
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
}
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,
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,
456 { "MAC Address", "nsh.tlv.bbf.mac",
457 FT_ETHER
, BASE_NONE
, NULL
, 0x0,
460 { &hf_nsh_bbf_network_instance
,
461 { "Network Instance", "nsh.tlv.bbf.network_instance",
462 FT_STRING
, BASE_NONE
, NULL
, 0x0,
465 { &hf_nsh_bbf_interface_id
,
466 { "Interface Identifier", "nsh.tlv.bbf.interface_id",
467 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
474 static int *ett
[] = {
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
);
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
517 * indent-tabs-mode: nil
520 * vi: set shiftwidth=4 tabstop=8 expandtab:
521 * :indentSize=4:tabSize=8:noTabs=true: