3 * Wireshark Protocol Analyzer Library
5 * Copyright (c) 2001 by Gerald Combs <gerald@wireshark.org>
7 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include <gnutls/gnutls.h>
19 #endif /* HAVE_LIBGNUTLS */
23 #include <wsutil/report_message.h>
25 #include <epan/exceptions.h>
27 #include "epan/frame_data.h"
29 #include "dfilter/dfilter.h"
30 #include "dfilter/dfilter-translator.h"
31 #include "epan_dissect.h"
33 #include <wsutil/nstime.h>
34 #include <wsutil/wslog.h>
35 #include <wsutil/ws_assert.h>
36 #include <wsutil/version_info.h>
38 #include "conversation.h"
42 #include "column-info.h"
44 #include "addr_resolv.h"
46 #include <epan/wmem_scopes.h>
49 #include "capture_dissectors.h"
50 #include "exported_pdu.h"
51 #include "export_object.h"
52 #include "stat_tap_ui.h"
54 #include "disabled_protos.h"
55 #include "decode_as.h"
56 #include "conversation_filter.h"
57 #include "conversation_table.h"
58 #include "reassemble.h"
59 #include "srt_table.h"
60 #include "stats_tree.h"
67 #include <wsutil/plugins.h>
72 #include <wslua/wslua.h>
90 #include <nghttp2/nghttp2.h>
94 #include <nghttp3/nghttp3.h>
98 #include <brotli/decode.h>
102 #include <libxml/xmlversion.h>
103 #include <libxml/parser.h>
110 static GSList
*epan_plugin_register_all_procotols
;
111 static GSList
*epan_plugin_register_all_handoffs
;
113 static wmem_allocator_t
*pinfo_pool_cache
;
115 /* Global variables holding the content of the corresponding environment variable
116 * to save fetching it repeatedly.
118 bool wireshark_abort_on_dissector_bug
;
119 bool wireshark_abort_on_too_many_items
;
122 /* Used for bookkeeping, includes all libwireshark plugin types (dissector, tap, epan). */
123 static plugins_t
*libwireshark_plugins
;
126 /* "epan_plugins" are a specific type of libwireshark plugin (the name isn't the best for clarity). */
127 static GSList
*epan_plugins
;
130 epan_get_version(void) {
135 epan_get_version_number(int *major
, int *minor
, int *micro
)
138 *major
= VERSION_MAJOR
;
140 *minor
= VERSION_MINOR
;
142 *micro
= VERSION_MICRO
;
145 #if defined(_WIN32) && GCRYPT_VERSION_NUMBER < 0x010b00
146 // Libgcrypt prints all log messages to stderr by default. This is noisier
147 // than we would like on Windows. In particular slow_gatherer tends to print
148 // "NOTE: you should run 'diskperf -y' to enable the disk statistics"
149 // which we don't care about.
150 // gcry_set_log_handler was deprecated in libgcrypt 1.11.0, and also that
151 // particular log message was quieted when not supported (and hence not useful)
152 // https://github.com/gpg/libgcrypt/commit/35abf4d2eb582b78873aa324f6d02976788ffbbc
154 quiet_gcrypt_logger (void *dummy _U_
, int level
, const char *format
, va_list args
)
156 enum ws_log_level log_level
;
159 case GCRY_LOG_CONT
: // Continuation. Ignore for now.
165 log_level
= LOG_LEVEL_WARNING
;
168 log_level
= LOG_LEVEL_ERROR
;
171 log_level
= LOG_LEVEL_CRITICAL
;
176 ws_logv(LOG_DOMAIN_EPAN
, log_level
, format
, args
);
181 epan_plugin_init(void *data
, void *user_data _U_
)
183 ((epan_plugin
*)data
)->init();
187 epan_plugin_post_init(void *data
, void *user_data _U_
)
189 ((epan_plugin
*)data
)->post_init();
193 epan_plugin_dissect_init(void *data
, void *user_data
)
195 ((epan_plugin
*)data
)->dissect_init((epan_dissect_t
*)user_data
);
199 epan_plugin_dissect_cleanup(void *data
, void *user_data
)
201 ((epan_plugin
*)data
)->dissect_cleanup((epan_dissect_t
*)user_data
);
205 epan_plugin_cleanup(void *data
, void *user_data _U_
)
207 ((epan_plugin
*)data
)->cleanup();
211 void epan_register_plugin(const epan_plugin
*plug
)
213 epan_plugins
= g_slist_prepend(epan_plugins
, (epan_plugin
*)plug
);
214 if (plug
->register_all_protocols
)
215 epan_plugin_register_all_procotols
= g_slist_prepend(epan_plugin_register_all_procotols
, plug
->register_all_protocols
);
216 if (plug
->register_all_handoffs
)
217 epan_plugin_register_all_handoffs
= g_slist_prepend(epan_plugin_register_all_handoffs
, plug
->register_all_handoffs
);
219 #else /* HAVE_PLUGINS */
220 void epan_register_plugin(const epan_plugin
*plug _U_
)
222 ws_warning("epan_register_plugin: built without support for binary plugins");
224 #endif /* HAVE_PLUGINS */
226 int epan_plugins_supported(void)
229 return plugins_supported() ? 0 : 1;
235 static void epan_plugin_register_all_tap_listeners(void *data
, void *user_data _U_
)
237 epan_plugin
*plug
= (epan_plugin
*)data
;
238 if (plug
->register_all_tap_listeners
)
239 plug
->register_all_tap_listeners();
243 epan_init(register_cb cb
, void *client_data
, bool load_plugins
)
245 volatile bool status
= true;
247 /* Get the value of some environment variables and set corresponding globals for performance reasons*/
248 /* If the WIRESHARK_ABORT_ON_DISSECTOR_BUG environment variable is set,
249 * it will call abort(), instead, to make it easier to get a stack trace.
251 if (getenv("WIRESHARK_ABORT_ON_DISSECTOR_BUG") != NULL
) {
252 wireshark_abort_on_dissector_bug
= true;
254 wireshark_abort_on_dissector_bug
= false;
257 if (getenv("WIRESHARK_ABORT_ON_TOO_MANY_ITEMS") != NULL
) {
258 wireshark_abort_on_too_many_items
= true;
260 wireshark_abort_on_too_many_items
= false;
264 * proto_init -> register_all_protocols -> g_async_queue_new which
265 * requires threads to be initialized. This happens automatically with
266 * GLib 2.32, before that g_thread_init must be called. But only since
267 * GLib 2.24, multiple invocations are allowed. Check for an earlier
268 * invocation just in case.
270 /* initialize memory allocation subsystem */
273 /* initialize the GUID to name mapping table */
276 /* initialize name resolution (addr_resolv.c) */
281 dfilter_translator_init();
285 libwireshark_plugins
= plugins_init(WS_PLUGIN_EPAN
);
289 /* initialize libgcrypt (beware, it won't be thread-safe) */
290 #if GCRYPT_VERSION_NUMBER >= 0x010a00
291 /* Ensure FIPS mode is disabled; it makes it impossible to decrypt
292 * non-NIST approved algorithms. We're decrypting, not promising
293 * security. This overrides any file or environment variables that
294 * would normally turn on FIPS mode, and has to be done prior to
295 * gcry_check_version().
297 gcry_control (GCRYCTL_NO_FIPS_MODE
);
299 gcry_check_version(NULL
);
300 #if defined(_WIN32) && GCRYPT_VERSION_NUMBER < 0x010b00
301 gcry_set_log_handler (quiet_gcrypt_logger
, NULL
);
303 gcry_control (GCRYCTL_DISABLE_SECMEM
, 0);
304 gcry_control (GCRYCTL_INITIALIZATION_FINISHED
, 0);
305 #ifdef HAVE_LIBGNUTLS
306 gnutls_global_init();
307 #if GNUTLS_VERSION_NUMBER >= 0x030602
308 if (gnutls_fips140_mode_enabled()) {
309 gnutls_fips140_set_mode(GNUTLS_FIPS140_LAX
, 0);
319 // We might receive a SIGPIPE due to maxmind_db.
320 signal(SIGPIPE
, SIG_IGN
);
331 capture_dissector_init();
332 reassembly_tables_init();
333 conversation_filters_init();
334 g_slist_foreach(epan_plugins
, epan_plugin_init
, NULL
);
335 proto_init(epan_plugin_register_all_procotols
, epan_plugin_register_all_handoffs
, cb
, client_data
);
336 g_slist_foreach(epan_plugins
, epan_plugin_register_all_tap_listeners
, NULL
);
337 packet_cache_proto_handles();
340 final_registration_all_protocols();
341 print_cache_field_handles();
342 expert_packet_init();
344 wslua_init(cb
, client_data
);
346 g_slist_foreach(epan_plugins
, epan_plugin_post_init
, NULL
);
348 CATCH(DissectorError
) {
350 * This is probably a dissector, or something it calls,
351 * calling REPORT_DISSECTOR_ERROR() in a registration
352 * routine or something else outside the normal dissection
355 const char *exception_message
= GET_MESSAGE
;
356 static const char dissector_error_nomsg
[] =
357 "Dissector writer didn't bother saying what the error was";
359 report_failure("Dissector bug: %s",
360 exception_message
== NULL
?
361 dissector_error_nomsg
: exception_message
);
362 if (getenv("WIRESHARK_ABORT_ON_DISSECTOR_BUG") != NULL
)
371 * Load all settings, from the current profile, that affect libwireshark.
374 epan_load_settings(void)
378 /* load the decode as entries of the current profile */
379 load_decode_as_entries();
381 prefs_p
= read_prefs();
384 * Read the files that enable and disable protocols and heuristic
387 read_enabled_and_disabled_lists();
395 g_slist_foreach(epan_plugins
, epan_plugin_cleanup
, NULL
);
396 g_slist_free(epan_plugins
);
398 g_slist_free(epan_plugin_register_all_procotols
);
399 epan_plugin_register_all_procotols
= NULL
;
400 g_slist_free(epan_plugin_register_all_handoffs
);
401 epan_plugin_register_all_handoffs
= NULL
;
409 * Must deregister Proto objects in Lua before destroying dissector
410 * tables in packet_cleanup(). Doing so will also deregister and free
411 * preferences, this must happen before prefs_cleanup(). That will
412 * update the list of deregistered fields which must be followed by
413 * proto_cleanup() to complete deallocation.
415 wslua_early_cleanup();
419 * Note: packet_cleanup() will call registered shutdown routines which
420 * may be used to deregister dynamically registered protocol fields,
421 * and prefs_cleanup() will call uat_clear() which also may be used to
422 * deregister dynamically registered protocol fields. This must be done
423 * before proto_cleanup() to avoid inconsistency and memory leaks.
430 conversation_filters_cleanup();
431 reassembly_table_cleanup();
434 capture_dissector_cleanup();
435 export_pdu_cleanup();
436 cleanup_enabled_and_disabled_lists();
437 stats_tree_cleanup();
443 #ifdef HAVE_LIBGNUTLS
444 gnutls_global_deinit();
450 addr_resolv_cleanup();
452 dfilter_translator_cleanup();
454 if (pinfo_pool_cache
!= NULL
) {
455 wmem_destroy_allocator(pinfo_pool_cache
);
456 pinfo_pool_cache
= NULL
;
459 wmem_cleanup_scopes();
462 plugins_cleanup(libwireshark_plugins
);
463 libwireshark_plugins
= NULL
;
467 struct epan_session
{
468 struct packet_provider_data
*prov
; /* packet provider data for this session */
469 struct packet_provider_funcs funcs
; /* functions using that data */
473 epan_new(struct packet_provider_data
*prov
,
474 const struct packet_provider_funcs
*funcs
)
476 epan_t
*session
= g_slice_new0(epan_t
);
478 session
->prov
= prov
;
479 session
->funcs
= *funcs
;
481 /* XXX, it should take session as param */
488 epan_get_modified_block(const epan_t
*session
, const frame_data
*fd
)
490 if (session
->funcs
.get_modified_block
)
491 return session
->funcs
.get_modified_block(session
->prov
, fd
);
497 epan_get_interface_name(const epan_t
*session
, uint32_t interface_id
, unsigned section_number
)
499 if (session
->funcs
.get_interface_name
)
500 return session
->funcs
.get_interface_name(session
->prov
, interface_id
, section_number
);
506 epan_get_interface_description(const epan_t
*session
, uint32_t interface_id
, unsigned section_number
)
508 if (session
->funcs
.get_interface_description
)
509 return session
->funcs
.get_interface_description(session
->prov
, interface_id
, section_number
);
515 epan_get_frame_ts(const epan_t
*session
, uint32_t frame_num
)
517 const nstime_t
*abs_ts
= NULL
;
519 if (session
&& session
->funcs
.get_frame_ts
)
520 abs_ts
= session
->funcs
.get_frame_ts(session
->prov
, frame_num
);
523 /* This can happen if frame_num doesn't have a ts */
524 ws_debug("!!! couldn't get frame ts for %u !!!\n", frame_num
);
531 epan_free(epan_t
*session
)
534 /* XXX, it should take session as param */
535 cleanup_dissection();
537 g_slice_free(epan_t
, session
);
542 epan_conversation_init(void)
544 conversation_epan_reset();
547 /* Overrides proto_tree_visible i epan_dissect_init to make all fields visible.
548 * This is > 0 if a Lua script wanted to see all fields all the time.
549 * This is ref-counted, so clearing it won't override other taps/scripts wanting it.
551 static int always_visible_refcount
;
554 epan_set_always_visible(bool force
)
557 always_visible_refcount
++;
558 else if (always_visible_refcount
> 0)
559 always_visible_refcount
--;
563 epan_dissect_init(epan_dissect_t
*edt
, epan_t
*session
, const bool create_proto_tree
, const bool proto_tree_visible
)
567 edt
->session
= session
;
569 memset(&edt
->pi
, 0, sizeof(edt
->pi
));
570 if (pinfo_pool_cache
!= NULL
) {
571 edt
->pi
.pool
= pinfo_pool_cache
;
572 pinfo_pool_cache
= NULL
;
575 edt
->pi
.pool
= wmem_allocator_new(WMEM_ALLOCATOR_BLOCK_FAST
);
578 if (create_proto_tree
) {
579 edt
->tree
= proto_tree_create_root(&edt
->pi
);
580 proto_tree_set_visible(edt
->tree
, (always_visible_refcount
> 0) ? true : proto_tree_visible
);
588 g_slist_foreach(epan_plugins
, epan_plugin_dissect_init
, edt
);
592 epan_dissect_reset(epan_dissect_t
*edt
)
594 /* We have to preserve the pool pointer across the memzeroing */
595 wmem_allocator_t
*tmp
;
599 wtap_block_unref(edt
->pi
.rec
->block
);
601 g_slist_free(edt
->pi
.proto_data
);
603 /* Free the data sources list. */
604 free_data_sources(&edt
->pi
);
607 /* Free all tvb's chained from this tvb */
608 tvb_free_chain(edt
->tvb
);
613 proto_tree_reset(edt
->tree
);
618 memset(&edt
->pi
, 0, sizeof(edt
->pi
));
623 epan_dissect_new(epan_t
*session
, const bool create_proto_tree
, const bool proto_tree_visible
)
627 edt
= g_new0(epan_dissect_t
, 1);
629 epan_dissect_init(edt
, session
, create_proto_tree
, proto_tree_visible
);
634 epan_dissect_fake_protocols(epan_dissect_t
*edt
, const bool fake_protocols
)
637 proto_tree_set_fake_protocols(edt
->tree
, fake_protocols
);
641 epan_dissect_run(epan_dissect_t
*edt
, int file_type_subtype
,
642 wtap_rec
*rec
, tvbuff_t
*tvb
, frame_data
*fd
,
646 wslua_prime_dfilter(edt
); /* done before entering wmem scope */
648 wmem_enter_packet_scope();
649 dissect_record(edt
, file_type_subtype
, rec
, tvb
, fd
, cinfo
);
651 /* free all memory allocated */
652 wmem_leave_packet_scope();
653 wtap_block_unref(rec
->block
);
658 epan_dissect_run_with_taps(epan_dissect_t
*edt
, int file_type_subtype
,
659 wtap_rec
*rec
, tvbuff_t
*tvb
, frame_data
*fd
,
662 wmem_enter_packet_scope();
664 dissect_record(edt
, file_type_subtype
, rec
, tvb
, fd
, cinfo
);
665 tap_push_tapped_queue(edt
);
667 /* free all memory allocated */
668 wmem_leave_packet_scope();
669 wtap_block_unref(rec
->block
);
674 epan_dissect_file_run(epan_dissect_t
*edt
, wtap_rec
*rec
,
675 tvbuff_t
*tvb
, frame_data
*fd
, column_info
*cinfo
)
678 wslua_prime_dfilter(edt
); /* done before entering wmem scope */
680 wmem_enter_packet_scope();
681 dissect_file(edt
, rec
, tvb
, fd
, cinfo
);
683 /* free all memory allocated */
684 wmem_leave_packet_scope();
685 wtap_block_unref(rec
->block
);
690 epan_dissect_file_run_with_taps(epan_dissect_t
*edt
, wtap_rec
*rec
,
691 tvbuff_t
*tvb
, frame_data
*fd
, column_info
*cinfo
)
693 wmem_enter_packet_scope();
695 dissect_file(edt
, rec
, tvb
, fd
, cinfo
);
696 tap_push_tapped_queue(edt
);
698 /* free all memory allocated */
699 wmem_leave_packet_scope();
700 wtap_block_unref(rec
->block
);
705 epan_dissect_cleanup(epan_dissect_t
* edt
)
709 g_slist_foreach(epan_plugins
, epan_plugin_dissect_cleanup
, edt
);
711 g_slist_free(edt
->pi
.proto_data
);
713 /* Free the data sources list. */
714 free_data_sources(&edt
->pi
);
717 /* Free all tvb's chained from this tvb */
718 tvb_free_chain(edt
->tvb
);
722 proto_tree_free(edt
->tree
);
725 if (pinfo_pool_cache
== NULL
) {
726 wmem_free_all(edt
->pi
.pool
);
727 pinfo_pool_cache
= edt
->pi
.pool
;
730 wmem_destroy_allocator(edt
->pi
.pool
);
735 epan_dissect_free(epan_dissect_t
* edt
)
737 epan_dissect_cleanup(edt
);
742 epan_dissect_prime_with_dfilter(epan_dissect_t
*edt
, const dfilter_t
* dfcode
)
744 dfilter_prime_proto_tree(dfcode
, edt
->tree
);
748 epan_dissect_prime_with_dfilter_print(epan_dissect_t
*edt
, const dfilter_t
* dfcode
)
750 dfilter_prime_proto_tree_print(dfcode
, edt
->tree
);
754 epan_dissect_prime_with_hfid(epan_dissect_t
*edt
, int hfid
)
756 proto_tree_prime_with_hfid(edt
->tree
, hfid
);
760 epan_dissect_prime_with_hfid_array(epan_dissect_t
*edt
, GArray
*hfids
)
764 for (i
= 0; i
< hfids
->len
; i
++) {
765 proto_tree_prime_with_hfid(edt
->tree
,
766 g_array_index(hfids
, int, i
));
770 /* ----------------------- */
772 epan_custom_set(epan_dissect_t
*edt
, GSList
*field_ids
,
774 bool display_details
,
776 char *expr
, const int size
)
778 return proto_custom_set(edt
->tree
, field_ids
, occurrence
, display_details
, result
, expr
, size
);
782 epan_dissect_fill_in_columns(epan_dissect_t
*edt
, const bool fill_col_exprs
, const bool fill_fd_colums
)
784 col_custom_set_edt(edt
, edt
->pi
.cinfo
);
785 col_fill_in(&edt
->pi
, fill_col_exprs
, fill_fd_colums
);
789 epan_dissect_packet_contains_field(epan_dissect_t
* edt
,
790 const char *field_name
)
796 if (!edt
|| !edt
->tree
)
798 field_id
= proto_get_id_by_filter_name(field_name
);
801 array
= proto_find_finfo(edt
->tree
, field_id
);
802 contains_field
= (array
->len
> 0) ? true : false;
803 g_ptr_array_free(array
, true);
804 return contains_field
;
808 * Get compile-time information for libraries used by libwireshark.
811 epan_gather_compile_info(feature_list l
)
813 gather_zlib_compile_info(l
);
814 gather_zlib_ng_compile_info(l
);
815 gather_pcre2_compile_info(l
);
819 #ifdef HAVE_LUA_UNICODE
820 with_feature(l
, "%s", LUA_RELEASE
" (with UfW patches)");
821 #else /* HAVE_LUA_UNICODE */
822 with_feature(l
, "%s", LUA_RELEASE
);
823 #endif /* HAVE_LUA_UNICODE */
825 without_feature(l
, "Lua");
826 #endif /* HAVE_LUA */
829 #ifdef HAVE_LIBGNUTLS
830 #ifdef HAVE_GNUTLS_PKCS11
831 with_feature(l
, "GnuTLS %s and PKCS #11 support", LIBGNUTLS_VERSION
);
833 with_feature(l
, "GnuTLS %s", LIBGNUTLS_VERSION
);
834 #endif /* HAVE_GNUTLS_PKCS11 */
836 without_feature(l
, "GnuTLS");
837 #endif /* HAVE_LIBGNUTLS */
840 with_feature(l
, "Gcrypt %s", GCRYPT_VERSION
);
843 #if defined(HAVE_MIT_KERBEROS)
844 with_feature(l
, "Kerberos (MIT)");
845 #elif defined(HAVE_HEIMDAL_KERBEROS)
846 with_feature(l
, "Kerberos (Heimdal)");
848 without_feature(l
, "Kerberos");
849 #endif /* HAVE_KERBEROS */
852 #ifdef HAVE_MAXMINDDB
853 with_feature(l
, "MaxMind");
855 without_feature(l
, "MaxMind");
856 #endif /* HAVE_MAXMINDDB */
860 with_feature(l
, "nghttp2 %s", NGHTTP2_VERSION
);
862 without_feature(l
, "nghttp2");
863 #endif /* HAVE_NGHTTP2 */
867 with_feature(l
, "nghttp3 %s", NGHTTP3_VERSION
);
869 without_feature(l
, "nghttp3");
870 #endif /* HAVE_NGHTTP3 */
874 with_feature(l
, "brotli");
876 without_feature(l
, "brotli");
877 #endif /* HAVE_BROTLI */
881 with_feature(l
, "LZ4");
883 without_feature(l
, "LZ4");
884 #endif /* HAVE_LZ4 */
888 with_feature(l
, "Zstandard");
890 without_feature(l
, "Zstandard");
891 #endif /* HAVE_ZSTD */
895 with_feature(l
, "Snappy");
897 without_feature(l
, "Snappy");
898 #endif /* HAVE_SNAPPY */
902 with_feature(l
, "libxml2 %s", LIBXML_DOTTED_VERSION
);
904 without_feature(l
, "libxml2");
905 #endif /* HAVE_LIBXML2 */
909 with_feature(l
, "libsmi %s", SMI_VERSION_STRING
);
911 without_feature(l
, "libsmi");
912 #endif /* HAVE_LIBSMI */
916 * Get runtime information for libraries used by libwireshark.
919 epan_gather_runtime_info(feature_list l
)
921 gather_zlib_runtime_info(l
);
922 gather_pcre2_runtime_info(l
);
925 with_feature(l
, "c-ares %s", ares_version(NULL
));
928 #ifdef HAVE_LIBGNUTLS
929 with_feature(l
, "GnuTLS %s", gnutls_check_version(NULL
));
930 #endif /* HAVE_LIBGNUTLS */
933 with_feature(l
, "Gcrypt %s", gcry_check_version(NULL
));
936 #if NGHTTP2_VERSION_AGE >= 1
937 nghttp2_info
*nghttp2_ptr
= nghttp2_version(0);
938 with_feature(l
, "nghttp2 %s", nghttp2_ptr
->version_str
);
939 #endif /* NGHTTP2_VERSION_AGE */
942 #if NGHTTP3_VERSION_AGE >= 1
943 const nghttp3_info
*nghttp3_ptr
= nghttp3_version(0);
944 with_feature(l
, "nghttp3 %s", nghttp3_ptr
->version_str
);
945 #endif /* NGHTTP3_VERSION_AGE */
949 with_feature(l
, "brotli %d.%d.%d", BrotliDecoderVersion() >> 24,
950 (BrotliDecoderVersion() >> 12) & 0xFFF, BrotliDecoderVersion() & 0xFFF);
954 #if LZ4_VERSION_NUMBER >= 10703
955 with_feature(l
, "LZ4 %s", LZ4_versionString());
956 #endif /* LZ4_VERSION_NUMBER */
959 #if ZSTD_VERSION_NUMBER >= 10300
960 with_feature(l
, "Zstandard %s", ZSTD_versionString());
961 #endif /* ZSTD_VERSION_NUMBER */
964 #ifdef HAVE_SMI_VERSION_STRING
965 with_feature(l
, "libsmi %s", smi_version_string
);
966 #endif /* HAVE_SMI_VERSION_STRING */
970 * Editor modelines - https://www.wireshark.org/tools/modelines.html
975 * indent-tabs-mode: t
978 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
979 * :indentSize=8:tabSize=8:noTabs=false: