2 * Object IDentifier Support
4 * (c) 2007, Luis E. Garcia Ontanon <luis@ontanon.org>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
19 #include <wsutil/report_message.h>
21 #include <epan/strutil.h>
22 #include <epan/wmem_scopes.h>
27 #include "wsutil/filesystem.h"
28 #include "dissectors/packet-ber.h"
29 #include <wsutil/ws_assert.h>
34 static bool smi_init_done
;
35 static bool oids_init_done
;
36 static bool load_smi_modules
;
37 static bool suppress_smi_errors
;
40 #define D(level,args) do if (debuglevel >= level) { printf args; printf("\n"); fflush(stdout); } while(0)
44 static int debuglevel
;
47 * From SNMPv2-SMI and X.690
49 * Counter32 ::= [APPLICATION 1] IMPLICIT INTEGER (0..4294967295)
50 * Gauge32 ::= [APPLICATION 2] IMPLICIT INTEGER (0..4294967295)
51 * Unsigned32 ::= [APPLICATION 2] IMPLICIT INTEGER (0..4294967295) (alias of Gauge32)
52 * TimeTicks ::= [APPLICATION 3] IMPLICIT INTEGER (0..4294967295)
54 * If the BER encoding should not have the top bit set as to not become a negative number
55 * the BER encoding may take 5 octets to encode.
59 static const oid_value_type_t integer_type
= { FT_INT32
, BASE_DEC
, BER_CLASS_UNI
, BER_UNI_TAG_INTEGER
, 1, 4, OID_KEY_TYPE_INTEGER
, 1};
60 static const oid_value_type_t bytes_type
= { FT_BYTES
, BASE_SHOW_ASCII_PRINTABLE
, BER_CLASS_UNI
, BER_UNI_TAG_OCTETSTRING
, 0, -1, OID_KEY_TYPE_BYTES
, 0};
61 static const oid_value_type_t oid_type
= { FT_OID
, BASE_NONE
, BER_CLASS_UNI
, BER_UNI_TAG_OID
, 1, -1, OID_KEY_TYPE_OID
, 0};
62 static const oid_value_type_t ipv4_type
= { FT_IPv4
, BASE_NONE
, BER_CLASS_APP
, 0, 4, 4, OID_KEY_TYPE_IPADDR
, 4};
63 static const oid_value_type_t counter32_type
= { FT_UINT64
, BASE_DEC
, BER_CLASS_APP
, 1, 1, 5, OID_KEY_TYPE_INTEGER
, 1};
64 static const oid_value_type_t unsigned32_type
= { FT_UINT64
, BASE_DEC
, BER_CLASS_APP
, 2, 1, 5, OID_KEY_TYPE_INTEGER
, 1};
65 static const oid_value_type_t timeticks_type
= { FT_UINT64
, BASE_DEC
, BER_CLASS_APP
, 3, 1, 5, OID_KEY_TYPE_INTEGER
, 1};
67 static const oid_value_type_t opaque_type
= { FT_BYTES
, BASE_NONE
, BER_CLASS_APP
, 4, 1, 4, OID_KEY_TYPE_BYTES
, 0};
69 static const oid_value_type_t nsap_type
= { FT_BYTES
, BASE_NONE
, BER_CLASS_APP
, 5, 0, -1, OID_KEY_TYPE_NSAP
, 0};
70 static const oid_value_type_t counter64_type
= { FT_UINT64
, BASE_DEC
, BER_CLASS_APP
, 6, 1, 8, OID_KEY_TYPE_INTEGER
, 1};
71 static const oid_value_type_t ipv6_type
= { FT_IPv6
, BASE_NONE
, BER_CLASS_UNI
, BER_UNI_TAG_OCTETSTRING
, 16, 16, OID_KEY_TYPE_BYTES
, 16};
72 static const oid_value_type_t float_type
= { FT_FLOAT
, BASE_DEC
, BER_CLASS_UNI
, BER_UNI_TAG_OCTETSTRING
, 4, 4, OID_KEY_TYPE_WRONG
, 0};
73 static const oid_value_type_t double_type
= { FT_DOUBLE
, BASE_DEC
, BER_CLASS_UNI
, BER_UNI_TAG_OCTETSTRING
, 8, 8, OID_KEY_TYPE_WRONG
, 0};
74 static const oid_value_type_t ether_type
= { FT_ETHER
, BASE_NONE
, BER_CLASS_UNI
, BER_UNI_TAG_OCTETSTRING
, 6, 6, OID_KEY_TYPE_ETHER
, 6};
75 static const oid_value_type_t string_type
= { FT_STRING
, BASE_NONE
, BER_CLASS_UNI
, BER_UNI_TAG_OCTETSTRING
, 0, -1, OID_KEY_TYPE_STRING
, 0};
76 static const oid_value_type_t date_and_time_type
= { FT_STRING
, BASE_NONE
, BER_CLASS_UNI
, BER_UNI_TAG_OCTETSTRING
, 8, 11, OID_KEY_TYPE_DATE_AND_TIME
, 0};
77 #endif /* HAVE_LIBSMI */
79 static const oid_value_type_t unknown_type
= { FT_BYTES
, BASE_NONE
, BER_CLASS_ANY
, BER_TAG_ANY
, 0, -1, OID_KEY_TYPE_WRONG
, 0};
81 static oid_info_t oid_root
= { 0, NULL
, OID_KIND_UNKNOWN
, NULL
, &unknown_type
, -2, NULL
, NULL
, NULL
};
83 // NOLINTNEXTLINE(misc-no-recursion)
84 static void prepopulate_oids(void) {
85 if (!oid_root
.children
) {
86 char* debug_env
= getenv("WIRESHARK_DEBUG_MIBS");
89 debuglevel
= debug_env
? (int)strtoul(debug_env
,NULL
,10) : 0;
91 oid_root
.children
= wmem_tree_new(wmem_epan_scope());
94 * make sure we got strings at least in the three root-children oids
95 * that way oid_resolved() will always have a string to print
97 // We recurse here once.
98 subid
= 0; oid_add("itu-t",1,&subid
);
99 subid
= 1; oid_add("iso",1,&subid
);
100 subid
= 2; oid_add("joint-iso-itu-t",1,&subid
);
104 // NOLINTNEXTLINE(misc-no-recursion)
105 static oid_info_t
* add_oid(const char* name
, oid_kind_t kind
, const oid_value_type_t
* type
, oid_key_t
* key
, unsigned oid_len
, uint32_t *subids
) {
107 oid_info_t
* c
= &oid_root
;
113 oid_info_t
* n
= (oid_info_t
*)wmem_tree_lookup32(c
->children
,subids
[i
]);
118 if (!g_str_equal(n
->name
,name
)) {
119 D(2,("Renaming Oid from: %s -> %s, this means the same oid is registered more than once",n
->name
,name
));
121 wmem_free(wmem_epan_scope(), n
->name
);
124 n
->name
= wmem_strdup(wmem_epan_scope(), name
);
126 if (! n
->value_type
) {
127 n
->value_type
= type
;
133 n
= wmem_new(wmem_epan_scope(), oid_info_t
);
134 n
->subid
= subids
[i
];
136 n
->children
= wmem_tree_new(wmem_epan_scope());
142 wmem_tree_insert32(c
->children
,n
->subid
,n
);
145 n
->name
= wmem_strdup(wmem_epan_scope(), name
);
146 n
->value_type
= type
;
151 n
->value_type
= NULL
;
152 n
->kind
= OID_KIND_UNKNOWN
;
158 ws_assert_not_reached();
162 // NOLINTNEXTLINE(misc-no-recursion)
163 void oid_add(const char* name
, unsigned oid_len
, uint32_t *subids
) {
164 ws_assert(subids
&& *subids
<= 2);
166 char* sub
= oid_subid2string(NULL
, subids
,oid_len
);
167 D(3,("\tOid (from subids): %s %s ",name
?name
:"NULL", sub
));
168 add_oid(name
,OID_KIND_UNKNOWN
,NULL
,NULL
,oid_len
,subids
);
169 wmem_free(NULL
, sub
);
171 D(1,("Failed to add Oid: %s (from subids)",name
?name
:"NULL"));
175 void oid_add_from_string(const char* name
, const char *oid_str
) {
177 unsigned oid_len
= oid_string2subid(NULL
, oid_str
, &subids
);
180 char* sub
= oid_subid2string(NULL
, subids
,oid_len
);
181 D(3,("\tOid (from string): %s %s ",name
?name
:"NULL", sub
));
182 add_oid(name
,OID_KIND_UNKNOWN
,NULL
,NULL
,oid_len
,subids
);
183 wmem_free(NULL
, sub
);
185 D(1,("Failed to add Oid: %s %s ",name
?name
:"NULL", oid_str
?oid_str
:NULL
));
187 wmem_free(NULL
, subids
);
190 extern void oid_add_from_encoded(const char* name
, const uint8_t *oid
, int oid_len
) {
191 uint32_t* subids
= NULL
;
192 unsigned subids_len
= oid_encoded2subid(NULL
, oid
, oid_len
, &subids
);
195 char* sub
= oid_subid2string(NULL
, subids
,subids_len
);
196 D(3,("\tOid (from encoded): %s %s ",name
, sub
));
197 add_oid(name
,OID_KIND_UNKNOWN
,NULL
,NULL
,subids_len
,subids
);
198 wmem_free(NULL
, sub
);
200 char* bytestr
= bytes_to_str_punct(NULL
, oid
, oid_len
, ':');
201 D(1,("Failed to add Oid: %s [%d]%s ",name
?name
:"NULL", oid_len
, bytestr
));
202 wmem_free(NULL
, bytestr
);
204 wmem_free(NULL
, subids
);
208 /* de-allocate storage mallocated by libsmi */
210 /* XXX: libsmi provides access to smiFree as of libsmi v 0.4.8. */
211 /* On Windows: Wireshark 1.01 and later is built and distributed */
212 /* with libsmi 0.4.8 (or newer). */
213 /* On non-Windows systems, free() should be OK for libsmi */
214 /* versions older than 0.4.8. */
216 static void smi_free(void *ptr
) {
218 #if (SMI_VERSION_MAJOR > 0) || (SMI_VERSION_MINOR > 4) || (SMI_VERSION_PATCHLEVEL >= 8)
222 #error Unsupported Windows libsmi version < 0.4.8
224 #define xx_free free /* hack so checkAPIs.pl doesn't complain */
230 typedef struct smi_module_t
{
234 static smi_module_t
* smi_paths
;
235 static unsigned num_smi_paths
;
236 static uat_t
* smi_paths_uat
;
238 static smi_module_t
* smi_modules
;
239 static unsigned num_smi_modules
;
240 static uat_t
* smi_modules_uat
;
242 static GString
* smi_errors
;
244 UAT_DIRECTORYNAME_CB_DEF(smi_mod
,name
,smi_module_t
)
246 static void smi_error_handler(char *path
, int line
, int severity
, char *msg
, char *tag
) {
247 g_string_append_printf(smi_errors
,"%s:%d %d %s %s\n",
255 static void* smi_mod_copy_cb(void* dest
, const void* orig
, size_t len _U_
) {
256 const smi_module_t
* m
= (const smi_module_t
*)orig
;
257 smi_module_t
* d
= (smi_module_t
*)dest
;
259 d
->name
= g_strdup(m
->name
);
264 static void smi_mod_free_cb(void* p
) {
265 smi_module_t
* m
= (smi_module_t
*)p
;
270 static char* alnumerize(const char* name
) {
271 char* s
= g_strdup(name
);
276 for (;(c
= *r
); r
++) {
277 if (g_ascii_isalnum(c
) || c
== '_' || c
== '-' || c
== '.') {
279 } else if (c
== ':' && r
[1] == ':') {
289 static const oid_value_type_t
* get_typedata(SmiType
* smiType
) {
291 * There has to be a better way to know if a given
292 * OCTETSTRING type is actually human readable text,
293 * an address of some type or some moe specific FT_
294 * Until that is found, this is the mappping between
295 * SNMP Types and our FT_s
297 static const struct _type_mapping_t
{
300 const oid_value_type_t
* type
;
302 {"IpAddress", SMI_BASETYPE_UNKNOWN
, &ipv4_type
},
303 {"InetAddressIPv4",SMI_BASETYPE_UNKNOWN
,&ipv4_type
},
304 {"InetAddressIPv6",SMI_BASETYPE_UNKNOWN
,&ipv6_type
},
305 {"NetworkAddress",SMI_BASETYPE_UNKNOWN
,&ipv4_type
},
306 {"MacAddress",SMI_BASETYPE_UNKNOWN
,ðer_type
},
307 {"TimeTicks",SMI_BASETYPE_UNKNOWN
,&timeticks_type
},
308 {"Ipv6Address",SMI_BASETYPE_UNKNOWN
,&ipv6_type
},
309 {"TimeStamp",SMI_BASETYPE_UNKNOWN
,&timeticks_type
},
310 {"DisplayString",SMI_BASETYPE_UNKNOWN
,&string_type
},
311 {"SnmpAdminString",SMI_BASETYPE_UNKNOWN
,&string_type
},
312 {"DateAndTime",SMI_BASETYPE_UNKNOWN
,&date_and_time_type
},
313 {"Counter",SMI_BASETYPE_UNKNOWN
,&counter32_type
},
314 {"Counter32",SMI_BASETYPE_UNKNOWN
,&counter32_type
},
315 {"Unsigned32",SMI_BASETYPE_UNKNOWN
,&unsigned32_type
},
316 {"Gauge",SMI_BASETYPE_UNKNOWN
,&unsigned32_type
},
317 {"Gauge32",SMI_BASETYPE_UNKNOWN
,&unsigned32_type
},
318 {"NsapAddress",SMI_BASETYPE_UNKNOWN
,&nsap_type
},
319 {"i32",SMI_BASETYPE_INTEGER32
,&integer_type
},
320 {"octets",SMI_BASETYPE_OCTETSTRING
,&bytes_type
},
321 {"oid",SMI_BASETYPE_OBJECTIDENTIFIER
,&oid_type
},
322 {"u32",SMI_BASETYPE_UNSIGNED32
,&unsigned32_type
},
323 {"u64",SMI_BASETYPE_UNSIGNED64
,&counter64_type
},
324 {"f32",SMI_BASETYPE_FLOAT32
,&float_type
},
325 {"f64",SMI_BASETYPE_FLOAT64
,&double_type
},
326 {"f128",SMI_BASETYPE_FLOAT128
,&bytes_type
},
327 {"enum",SMI_BASETYPE_ENUM
,&integer_type
},
328 {"bits",SMI_BASETYPE_BITS
,&bytes_type
},
329 {"unk",SMI_BASETYPE_UNKNOWN
,&unknown_type
},
330 {NULL
,SMI_BASETYPE_UNKNOWN
,NULL
} /* SMI_BASETYPE_UNKNOWN = 0 */
332 const struct _type_mapping_t
* t
;
333 SmiType
* sT
= smiType
;
335 if (!smiType
) return NULL
;
338 for (t
= types
; t
->type
; t
++ ) {
339 char* name
= smiRenderType(sT
, SMI_RENDER_NAME
);
340 if (name
&& t
->name
&& g_str_equal(name
, t
->name
)) {
348 } while(( sT
= smiGetParentType(sT
) ));
350 for (t
= types
; t
->type
; t
++ ) {
351 if(smiType
->basetype
== t
->base
) {
356 return &unknown_type
;
359 static unsigned get_non_implicit_size(SmiType
* sT
) {
361 unsigned size
= 0xffffffff;
363 switch (sT
->basetype
) {
364 case SMI_BASETYPE_OCTETSTRING
:
365 case SMI_BASETYPE_OBJECTIDENTIFIER
:
371 for ( ; sT
; sT
= smiGetParentType(sT
) ) {
372 for (sR
= smiGetFirstRange(sT
); sR
; sR
= smiGetNextRange(sR
)) {
373 if (size
== 0xffffffff) {
374 if (sR
->minValue
.value
.unsigned32
== sR
->maxValue
.value
.unsigned32
) {
375 size
= (uint32_t)sR
->minValue
.value
.unsigned32
;
380 if (sR
->minValue
.value
.unsigned32
!= size
|| sR
->maxValue
.value
.unsigned32
!= size
) {
387 return size
== 0xffffffff ? 0 : size
;
391 static inline oid_kind_t
smikind(SmiNode
* sN
, oid_key_t
** key_p
) {
394 switch(sN
->nodekind
) {
395 case SMI_NODEKIND_ROW
: {
397 oid_key_t
* kl
= NULL
; /* points to last element in the list of oid_key_t's */
398 const oid_value_type_t
* typedata
= NULL
;
401 switch (sN
->indexkind
) {
402 case SMI_INDEX_INDEX
:
404 case SMI_INDEX_AUGMENT
:
405 case SMI_INDEX_REORDER
:
406 case SMI_INDEX_SPARSE
:
407 case SMI_INDEX_EXPAND
:
408 sN
= smiGetRelatedNode(sN
);
410 case SMI_INDEX_UNKNOWN
:
411 return OID_KIND_UNKNOWN
;
414 implied
= sN
->implied
;
416 for (sE
= smiGetFirstElement(sN
); sE
; sE
= smiGetNextElement(sE
)) {
417 SmiNode
* elNode
= smiGetElementNode(sE
) ;
418 SmiType
* elType
= smiGetNodeType(elNode
);
420 unsigned non_implicit_size
= 0;
424 non_implicit_size
= get_non_implicit_size(elType
);
427 typedata
= get_typedata(elType
);
429 k
= g_new(oid_key_t
,1);
431 oid1
= smiRenderOID(sN
->oidlen
, sN
->oid
, SMI_RENDER_QUALIFIED
);
432 oid2
= smiRenderOID(elNode
->oidlen
, elNode
->oid
, SMI_RENDER_NAME
);
433 k
->name
= g_strconcat(oid1
, ".", oid2
, NULL
);
438 k
->ft_type
= typedata
? typedata
->ft_type
: FT_BYTES
;
439 k
->display
= typedata
? typedata
->display
: BASE_NONE
;
444 k
->key_type
= typedata
->keytype
;
445 k
->num_subids
= typedata
->keysize
;
448 switch (elType
->basetype
) {
449 case SMI_BASETYPE_BITS
:
450 case SMI_BASETYPE_OCTETSTRING
: {
451 k
->key_type
= OID_KEY_TYPE_BYTES
;
452 k
->num_subids
= non_implicit_size
;
455 case SMI_BASETYPE_ENUM
:
456 case SMI_BASETYPE_OBJECTIDENTIFIER
:
457 case SMI_BASETYPE_INTEGER32
:
458 case SMI_BASETYPE_UNSIGNED32
:
459 case SMI_BASETYPE_INTEGER64
:
460 case SMI_BASETYPE_UNSIGNED64
:
461 k
->key_type
= OID_KEY_TYPE_INTEGER
;
465 k
->key_type
= OID_KEY_TYPE_WRONG
;
470 k
->key_type
= OID_KEY_TYPE_WRONG
;
477 * The list is empty, so set the
478 * pointer to the head of the list
479 * to point to this entry.
484 * The list is non-empty, and kl
485 * points to its last element.
486 * Make the last element point to
487 * this entry as its successor.
493 * This entry is now the last entry in
500 switch (kl
->key_type
) {
501 case OID_KEY_TYPE_BYTES
: kl
->key_type
= OID_KEY_TYPE_IMPLIED_BYTES
; break;
502 case OID_KEY_TYPE_STRING
: kl
->key_type
= OID_KEY_TYPE_IMPLIED_STRING
; break;
503 case OID_KEY_TYPE_OID
: kl
->key_type
= OID_KEY_TYPE_IMPLIED_OID
; break;
510 case SMI_NODEKIND_NODE
: return OID_KIND_NODE
;
511 case SMI_NODEKIND_SCALAR
: return OID_KIND_SCALAR
;
512 case SMI_NODEKIND_TABLE
: return OID_KIND_TABLE
;
513 case SMI_NODEKIND_COLUMN
: return OID_KIND_COLUMN
;
514 case SMI_NODEKIND_NOTIFICATION
: return OID_KIND_NOTIFICATION
;
515 case SMI_NODEKIND_GROUP
: return OID_KIND_GROUP
;
516 case SMI_NODEKIND_COMPLIANCE
: return OID_KIND_COMPLIANCE
;
517 case SMI_NODEKIND_CAPABILITIES
: return OID_KIND_CAPABILITIES
;
518 default: return OID_KIND_UNKNOWN
;
522 #define IS_ENUMABLE(ft) ( (ft == FT_UINT8) || (ft == FT_UINT16) || (ft == FT_UINT24) || (ft == FT_UINT32) \
523 || (ft == FT_INT8) || (ft == FT_INT16) || (ft == FT_INT24) || (ft == FT_INT32) \
524 || (ft == FT_UINT64) || (ft == FT_INT64) )
526 static void unregister_mibs(void) {
527 /* TODO: Unregister "MIBs" proto and clean up field array and subtree array.
528 * Wireshark does not support that yet. :-( */
533 static void restart_needed_warning(void) {
535 report_failure("Wireshark needs to be restarted for these changes to take effect");
538 static void register_mibs(void) {
539 SmiModule
*smiModule
;
547 if (!load_smi_modules
) {
548 D(1,("OID resolution not enabled"));
552 /* TODO: Remove this workaround when unregistration of "MIBs" proto is solved.
553 * Wireshark does not support that yet. :-( */
554 if (oids_init_done
) {
555 D(1,("Exiting register_mibs() to avoid double registration of MIBs proto."));
559 hfa
= wmem_array_new(wmem_epan_scope(), sizeof(hf_register_info
));
560 etta
= g_array_new(false,true,sizeof(int*));
562 smiInit("wireshark");
563 smi_init_done
= true;
565 smi_errors
= g_string_new("");
566 smiSetErrorHandler(smi_error_handler
);
568 path_str
= oid_get_default_mib_path();
569 D(1,("SMI Path: '%s'",path_str
));
571 smiSetPath(path_str
);
573 for(i
=0;i
<num_smi_modules
;i
++) {
574 if (!smi_modules
[i
].name
) continue;
576 if (smiIsLoaded(smi_modules
[i
].name
)) {
579 char* mod_name
= smiLoadModule(smi_modules
[i
].name
);
581 D(2,("Loaded: '%s'[%u] as %s",smi_modules
[i
].name
,i
,mod_name
));
583 D(1,("Failed to load: '%s'[%u]",smi_modules
[i
].name
,i
));
587 if (smi_errors
->len
) {
588 if (!suppress_smi_errors
) {
589 report_failure("The following errors were found while loading the MIBS:\n%s\n\n"
590 "The Current Path is: %s\n\nYou can avoid this error message "
591 "by removing the missing MIB modules at Edit -> Preferences"
592 " -> Name Resolution -> SMI (MIB and PIB) modules or by "
593 "installing them.\n" , smi_errors
->str
, path_str
);
595 D(1,("Errors while loading:\n%s\n",smi_errors
->str
));
599 g_string_free(smi_errors
,TRUE
);
601 for (smiModule
= smiGetFirstModule();
603 smiModule
= smiGetNextModule(smiModule
)) {
605 D(3,("\tModule: %s", smiModule
->name
));
607 /* TODO: Check libsmi version at compile time and disable this
608 * workaround for libsmi versions where this problem is fixed.
609 * Currently there is no such version. :-(
611 if (smiModule
->conformance
== 1) {
612 if (!suppress_smi_errors
) {
613 report_failure("Stopped processing module %s due to "
614 "error(s) to prevent potential crash in libsmi.\n"
615 "Module's conformance level: %d.\n"
616 "See details at: https://bugs.debian.org/560325\n",
617 smiModule
->name
, smiModule
->conformance
);
621 for (smiNode
= smiGetFirstNode(smiModule
, SMI_NODEKIND_ANY
);
623 smiNode
= smiGetNextNode(smiNode
, SMI_NODEKIND_ANY
)) {
625 SmiType
* smiType
= smiGetNodeType(smiNode
);
626 const oid_value_type_t
* typedata
= get_typedata(smiType
);
628 oid_kind_t kind
= smikind(smiNode
,&key
);
630 char *oid
= smiRenderOID(smiNode
->oidlen
, smiNode
->oid
, SMI_RENDER_QUALIFIED
);
631 oid_info_t
* oid_data
= add_oid(oid
,
639 sub
= oid_subid2string(NULL
, smiNode
->oid
, smiNode
->oidlen
);
640 D(4,("\t\tNode: kind=%d oid=%s name=%s ",
641 oid_data
->kind
, sub
, oid_data
->name
));
642 wmem_free(NULL
, sub
);
644 if ( typedata
&& oid_data
->value_hfid
== -2 ) {
645 SmiNamedNumber
* smiEnum
;
650 name
= g_strdup(oid_data
->name
);
651 blurb
= smiRenderOID(smiNode
->oidlen
, smiNode
->oid
, SMI_RENDER_ALL
);
652 /* Don't allow duplicate blurb/name */
653 if (strcmp(blurb
, name
) == 0) {
658 hf
.p_id
= &(oid_data
->value_hfid
);
659 hf
.hfinfo
.name
= name
;
660 hf
.hfinfo
.abbrev
= alnumerize(oid_data
->name
);
661 hf
.hfinfo
.type
= typedata
->ft_type
;
662 hf
.hfinfo
.display
= typedata
->display
;
663 hf
.hfinfo
.strings
= NULL
;
664 hf
.hfinfo
.bitmask
= 0;
665 hf
.hfinfo
.blurb
= blurb
;
669 oid_data
->value_hfid
= -1;
671 if ( IS_ENUMABLE(hf
.hfinfo
.type
) && (smiEnum
= smiGetFirstNamedNumber(smiType
))) {
672 GArray
* vals
= g_array_new(true,true,sizeof(value_string
));
674 for(;smiEnum
; smiEnum
= smiGetNextNamedNumber(smiEnum
)) {
677 val
.value
= (uint32_t)smiEnum
->value
.value
.integer32
;
678 val
.strptr
= g_strdup(smiEnum
->name
);
679 g_array_append_val(vals
,val
);
683 hf
.hfinfo
.strings
= g_array_free(vals
, false);
685 #if 0 /* packet-snmp does not handle bits yet */
686 } else if (smiType
->basetype
== SMI_BASETYPE_BITS
&& ( smiEnum
= smiGetFirstNamedNumber(smiType
) )) {
688 oid_bits_info_t
* bits
= g_new(oid_bits_info_t
, 1);
689 int* ettp
= &(bits
->ett
);
694 g_array_append_val(etta
,ettp
);
696 for(;smiEnum
; smiEnum
= smiGetNextNamedNumber(smiEnum
), bits
->num
++);
698 bits
->data
= g_malloc(sizeof(struct _oid_bit_t
)*bits
->num
);
700 for(smiEnum
= smiGetFirstNamedNumber(smiType
),n
=0;
702 smiEnum
= smiGetNextNamedNumber(smiEnum
),n
++) {
703 unsigned mask
= 1 << (smiEnum
->value
.value
.integer32
% 8);
704 char* base
= alnumerize(oid_data
->name
);
705 char* ext
= alnumerize(smiEnum
->name
);
706 hf_register_info hf2
= { &(bits
->data
[n
].hfid
), { NULL
, NULL
, FT_UINT8
, BASE_HEX
, NULL
, mask
, NULL
, HFILL
}};
708 bits
->data
[n
].hfid
= -1;
709 bits
->data
[n
].offset
= smiEnum
->value
.value
.integer32
/ 8;
711 hf2
.hfinfo
.name
= g_strconcat("%s:%s",oid_data
->name
, ":", smiEnum
->name
, NULL
);
712 hf2
.hfinfo
.abbrev
= g_strconcat(base
, ".", ext
, NULL
);
716 g_array_append_val(hfa
,hf2
);
718 #endif /* packet-snmp does not use this yet */
719 wmem_array_append_one(hfa
,hf
);
722 if ((key
= oid_data
->key
)) {
723 for(; key
; key
= key
->next
) {
724 D(5,("\t\t\tIndex: name=%s subids=%u key_type=%d",
725 key
->name
, key
->num_subids
, key
->key_type
));
727 if (key
->hfid
== -2) {
730 hf
.p_id
= &(key
->hfid
);
731 hf
.hfinfo
.name
= key
->name
;
732 hf
.hfinfo
.abbrev
= alnumerize(key
->name
);
733 hf
.hfinfo
.type
= key
->ft_type
;
734 hf
.hfinfo
.display
= key
->display
;
735 hf
.hfinfo
.strings
= NULL
;
736 hf
.hfinfo
.bitmask
= 0;
737 hf
.hfinfo
.blurb
= NULL
;
741 wmem_array_append_one(hfa
,hf
);
749 proto_mibs
= proto_register_protocol("MIBs", "MIBS", "mibs");
751 proto_register_field_array(proto_mibs
, (hf_register_info
*)wmem_array_get_raw(hfa
), wmem_array_get_count(hfa
));
753 proto_register_subtree_array((int**)(void*)etta
->data
, etta
->len
);
755 g_array_free(etta
,true);
757 oids_init_done
= true;
761 void oid_pref_init(module_t
*nameres
)
764 static uat_field_t smi_fields
[] = {
765 UAT_FLD_CSTRING(smi_mod
,name
,"Module name","The module's name"),
768 static uat_field_t smi_paths_fields
[] = {
769 UAT_FLD_DIRECTORYNAME(smi_mod
,name
,"Directory path","The directory name"),
773 prefs_register_bool_preference(nameres
, "load_smi_modules",
774 "Enable OID resolution",
775 "Resolve Object IDs to object names from the MIB and PIB"
776 " modules defined below."
777 " You must restart Wireshark for this change to take effect",
780 prefs_register_bool_preference(nameres
, "suppress_smi_errors",
781 "Suppress SMI errors",
782 "While loading MIB or PIB modules errors may be detected,"
783 " which are reported. Some errors can be ignored."
784 " If unsure, set to false.",
785 &suppress_smi_errors
);
787 smi_paths_uat
= uat_new("SMI Paths",
788 sizeof(smi_module_t
),
793 /* affects dissection of packets (as the MIBs and PIBs affect the
794 interpretation of e.g. SNMP variable bindings), but not set of
797 XXX - if named fields are generated from the MIBs and PIBs
798 for particular variable bindings, this *does* affect the set
800 UAT_AFFECTS_DISSECTION
,
805 restart_needed_warning
,
809 prefs_register_uat_preference(nameres
,
811 "SMI (MIB and PIB) paths",
812 "Search paths for SMI (MIB and PIB) modules. You must"
813 " restart Wireshark for these changes to take effect.",
816 smi_modules_uat
= uat_new("SMI Modules",
817 sizeof(smi_module_t
),
820 (void**)&smi_modules
,
822 /* affects dissection of packets (as the MIBs and PIBs affect the
823 interpretation of e.g. SNMP variable bindings), but not set of
826 XXX - if named fields are generated from the MIBs and PIBs
827 for particular variable bindings, would this affect the set
829 UAT_AFFECTS_DISSECTION
,
834 restart_needed_warning
,
838 prefs_register_uat_preference(nameres
,
840 "SMI (MIB and PIB) modules",
841 "List of SMI (MIB and PIB) modules to load. You must"
842 " restart Wireshark for these changes to take effect.",
846 prefs_register_static_text_preference(nameres
, "load_smi_modules_static",
847 "Enable OID resolution: N/A",
848 "Support for OID resolution was not compiled into this version of Wireshark");
850 prefs_register_static_text_preference(nameres
, "suppress_smi_errors_static",
851 "Suppress SMI errors: N/A",
852 "Support for OID resolution was not compiled into this version of Wireshark");
854 prefs_register_static_text_preference(nameres
, "smi_module_path",
855 "SMI (MIB and PIB) modules and paths: N/A",
856 "Support for OID resolution was not compiled into this version of Wireshark");
860 void oids_init(void) {
865 D(1,("libsmi disabled oid resolution not enabled"));
869 void oids_cleanup(void) {
873 D(1,("libsmi disabled oid resolution not enabled"));
877 char* oid_subid2string(wmem_allocator_t
*scope
, uint32_t* subids
, unsigned len
) {
878 return rel_oid_subid2string(scope
, subids
, len
, true);
880 char* rel_oid_subid2string(wmem_allocator_t
*scope
, uint32_t* subids
, unsigned len
, bool is_absolute
) {
882 wmem_strbuf_t
*oid_str
;
885 if(!subids
|| len
== 0)
886 return wmem_strdup(scope
, "*** Empty OID ***");
888 oid_str
= wmem_strbuf_new(scope
, "");
891 wmem_strbuf_append_c(oid_str
, '.');
894 wmem_strbuf_append_printf(oid_str
, "%u.",*subids
++);
897 /* Remove trailing "." (which is guaranteed to be there) */
898 oid_str_len
= wmem_strbuf_get_len(oid_str
);
899 wmem_strbuf_truncate(oid_str
, oid_str_len
- 1);
901 return wmem_strbuf_finalize(oid_str
);
904 /* If a valid OID string, return number of numbers */
905 static unsigned check_num_oid(const char* str
) {
910 D(8,("check_num_oid: '%s'",str
));
914 D(9,("\tcheck_num_oid: '%c' %u",*r
,n
));
918 if (c
== '.') return 0;
920 case '1' : case '2' : case '3' : case '4' : case '5' :
921 case '6' : case '7' : case '8' : case '9' : case '0' :
931 /* Set subids_p to an array of found numbers, return number of numbers */
932 unsigned oid_string2subid(wmem_allocator_t
*scope
, const char* str
, uint32_t** subids_p
) {
935 uint32_t* subids_overflow
;
936 unsigned n
= check_num_oid(str
);
938 * we cannot handle sub-ids greater than 32bytes
939 * keep a pilot subid of 64 bytes to check the limit
943 D(6,("oid_string2subid: str='%s'",str
));
950 *subids_p
= subids
= wmem_alloc0_array(scope
, uint32_t, n
);
951 subids_overflow
= subids
+ n
;
957 case '1' : case '2' : case '3' : case '4' : case '5' :
958 case '6' : case '7' : case '8' : case '9' : case '0' :
962 if( subids
>= subids_overflow
|| subid
> 0xffffffff) {
963 wmem_free(scope
, *subids_p
);
969 *(subids
) += *r
- '0';
981 unsigned oid_encoded2subid(wmem_allocator_t
*scope
, const uint8_t *oid_bytes
, int oid_len
, uint32_t** subids_p
) {
982 return oid_encoded2subid_sub(scope
, oid_bytes
, oid_len
, subids_p
, true);
984 unsigned oid_encoded2subid_sub(wmem_allocator_t
*scope
, const uint8_t *oid_bytes
, int oid_len
, uint32_t** subids_p
,
987 unsigned n
= is_first
? 1 : 0;
989 uint32_t* subid_overflow
;
991 * we cannot handle sub-ids greater than 32bytes
992 * have the subid in 64 bytes to be able to check the limit
996 for (i
=0; i
<oid_len
; i
++) { if (! (oid_bytes
[i
] & 0x80 )) n
++; }
998 *subids_p
= subids
= (uint32_t *)wmem_alloc(scope
, sizeof(uint32_t)*n
);
999 subid_overflow
= subids
+n
;
1001 /* If n is 0 or 1 (depending on how it was initialized) then we found
1002 * no bytes in the OID with first bit cleared, so initialize our one
1003 * byte (if any) to zero and return. This *seems* to be the right thing
1004 * to do in this situation, and at the very least it avoids
1005 * uninitialized memory errors that would otherwise occur. */
1006 if (is_first
&& n
== 1) {
1010 else if (!is_first
&& n
== 0) {
1014 for (i
=0; i
<oid_len
; i
++){
1015 uint8_t byte
= oid_bytes
[i
];
1018 subid
|= byte
& 0x7F;
1025 uint32_t subid0
= 0;
1027 if (subid
>= 40) { subid0
++; subid
-=40; }
1028 if (subid
>= 40) { subid0
++; subid
-=40; }
1035 if( subids
>= subid_overflow
|| subid
> 0xffffffff) {
1036 /* scope may be NULL in which case we must free our
1037 * useless buffer before returning */
1038 wmem_free(scope
, *subids_p
);
1043 *subids
++ = (uint32_t)subid
;
1047 ws_assert(subids
== subid_overflow
);
1052 oid_info_t
* oid_get(unsigned len
, uint32_t* subids
, unsigned* matched
, unsigned* left
) {
1053 oid_info_t
* curr_oid
= &oid_root
;
1056 if(!(subids
&& *subids
<= 2)) {
1062 for( i
=0; i
< len
; i
++) {
1063 oid_info_t
* next_oid
= (oid_info_t
*)wmem_tree_lookup32(curr_oid
->children
,subids
[i
]);
1065 curr_oid
= next_oid
;
1077 oid_info_t
* oid_get_from_encoded(wmem_allocator_t
*scope
, const uint8_t *bytes
, int byteslen
, uint32_t** subids_p
, unsigned* matched_p
, unsigned* left_p
) {
1078 unsigned subids_len
= oid_encoded2subid(scope
, bytes
, byteslen
, subids_p
);
1079 return oid_get(subids_len
, *subids_p
, matched_p
, left_p
);
1082 oid_info_t
* oid_get_from_string(wmem_allocator_t
*scope
, const char *oid_str
, uint32_t** subids_p
, unsigned* matched
, unsigned* left
) {
1083 unsigned subids_len
= oid_string2subid(scope
, oid_str
, subids_p
);
1084 return oid_get(subids_len
, *subids_p
, matched
, left
);
1087 char *oid_resolved_from_encoded(wmem_allocator_t
*scope
, const uint8_t *oid
, int oid_len
) {
1088 uint32_t *subid_oid
= NULL
;
1090 unsigned subid_oid_length
= oid_encoded2subid(NULL
, oid
, oid_len
, &subid_oid
);
1092 ret
= oid_resolved(scope
, subid_oid_length
, subid_oid
);
1093 wmem_free(NULL
, subid_oid
);
1097 char *rel_oid_resolved_from_encoded(wmem_allocator_t
*scope
, const uint8_t *oid
, int oid_len
) {
1098 uint32_t *subid_oid
= NULL
;
1100 unsigned subid_oid_length
= oid_encoded2subid_sub(NULL
, oid
, oid_len
, &subid_oid
, false);
1102 ret
= rel_oid_subid2string(scope
, subid_oid
, subid_oid_length
, false);
1103 wmem_free(NULL
, subid_oid
);
1108 unsigned oid_subid2encoded(wmem_allocator_t
*scope
, unsigned subids_len
, uint32_t* subids
, uint8_t** bytes_p
) {
1109 unsigned bytelen
= 0;
1114 if ( !subids
|| subids_len
<= 1) {
1119 for (subid
=subids
[0] * 40, i
= 1; i
<subids_len
; i
++, subid
=0) {
1121 if (subid
<= 0x0000007F) {
1123 } else if (subid
<= 0x00003FFF ) {
1125 } else if (subid
<= 0x001FFFFF ) {
1127 } else if (subid
<= 0x0FFFFFFF ) {
1134 *bytes_p
= b
= (uint8_t *)wmem_alloc(scope
, bytelen
);
1136 for (subid
=subids
[0] * 40, i
= 1; i
<subids_len
; i
++, subid
=0) {
1140 if ((subid
<= 0x0000007F )) len
= 1;
1141 else if ((subid
<= 0x00003FFF )) len
= 2;
1142 else if ((subid
<= 0x001FFFFF )) len
= 3;
1143 else if ((subid
<= 0x0FFFFFFF )) len
= 4;
1147 default: *bytes_p
=NULL
; return 0;
1148 case 5: *(b
++) = ((subid
& 0xF0000000) >> 28) | 0x80;
1150 case 4: *(b
++) = ((subid
& 0x0FE00000) >> 21) | 0x80;
1152 case 3: *(b
++) = ((subid
& 0x001FC000) >> 14) | 0x80;
1154 case 2: *(b
++) = ((subid
& 0x00003F80) >> 7) | 0x80;
1156 case 1: *(b
++) = subid
& 0x0000007F ; break;
1163 char* oid_encoded2string(wmem_allocator_t
*scope
, const uint8_t* encoded
, unsigned len
) {
1164 uint32_t* subids
= NULL
;
1166 unsigned subids_len
= oid_encoded2subid(NULL
, encoded
, len
, &subids
);
1169 ret
= oid_subid2string(scope
, subids
,subids_len
);
1171 ret
= wmem_strdup(scope
, "");
1174 wmem_free(NULL
, subids
);
1178 char* rel_oid_encoded2string(wmem_allocator_t
*scope
, const uint8_t* encoded
, unsigned len
) {
1179 uint32_t* subids
= NULL
;
1181 unsigned subids_len
= oid_encoded2subid_sub(NULL
, encoded
, len
, &subids
, false);
1184 ret
= rel_oid_subid2string(scope
, subids
,subids_len
, false);
1186 ret
= wmem_strdup(scope
, "");
1189 wmem_free(NULL
, subids
);
1193 unsigned oid_string2encoded(wmem_allocator_t
*scope
, const char *oid_str
, uint8_t **bytes
) {
1195 uint32_t subids_len
;
1198 if ( (subids_len
= oid_string2subid(NULL
, oid_str
, &subids
)) &&
1199 (byteslen
= oid_subid2encoded(scope
, subids_len
, subids
, bytes
)) ) {
1200 wmem_free(NULL
, subids
);
1203 wmem_free(NULL
, subids
);
1207 char *oid_resolved_from_string(wmem_allocator_t
*scope
, const char *oid_str
) {
1208 uint32_t *subid_oid
;
1209 unsigned subid_oid_length
;
1212 subid_oid_length
= oid_string2subid(NULL
, oid_str
, &subid_oid
);
1213 resolved
= oid_resolved(scope
, subid_oid_length
, subid_oid
);
1215 wmem_free(NULL
, subid_oid
);
1220 char *oid_resolved(wmem_allocator_t
*scope
, uint32_t num_subids
, uint32_t* subids
) {
1225 if(! (subids
&& *subids
<= 2 ))
1226 return wmem_strdup(scope
, "*** Malformed OID ***");
1228 oid
= oid_get(num_subids
, subids
, &matched
, &left
);
1230 while (! oid
->name
) {
1231 if (!(oid
= oid
->parent
)) {
1232 return oid_subid2string(scope
, subids
,num_subids
);
1240 *str1
= oid_subid2string(NULL
, subids
,matched
),
1241 *str2
= oid_subid2string(NULL
, &(subids
[matched
]),left
);
1243 ret
= wmem_strconcat(scope
, oid
->name
? oid
->name
: str1
, ".", str2
, NULL
);
1244 wmem_free(NULL
, str1
);
1245 wmem_free(NULL
, str2
);
1248 return oid
->name
? wmem_strdup(scope
, oid
->name
) : oid_subid2string(scope
, subids
,matched
);
1252 extern void oid_both(wmem_allocator_t
*scope
, unsigned oid_len
, uint32_t *subids
, char** resolved_p
, char** numeric_p
) {
1253 *resolved_p
= oid_resolved(scope
, oid_len
,subids
);
1254 *numeric_p
= oid_subid2string(scope
, subids
,oid_len
);
1257 extern void oid_both_from_encoded(wmem_allocator_t
*scope
, const uint8_t *oid
, int oid_len
, char** resolved_p
, char** numeric_p
) {
1258 uint32_t* subids
= NULL
;
1259 unsigned subids_len
= oid_encoded2subid(NULL
, oid
, oid_len
, &subids
);
1260 *resolved_p
= oid_resolved(scope
, subids_len
,subids
);
1261 *numeric_p
= oid_subid2string(scope
, subids
,subids_len
);
1262 wmem_free(NULL
, subids
);
1265 void oid_both_from_string(wmem_allocator_t
*scope
, const char *oid_str
, char** resolved_p
, char** numeric_p
) {
1267 unsigned subids_len
;
1269 subids_len
= oid_string2subid(NULL
, oid_str
, &subids
);
1270 *resolved_p
= oid_resolved(scope
, subids_len
,subids
);
1271 *numeric_p
= oid_subid2string(scope
, subids
,subids_len
);
1272 wmem_free(NULL
, subids
);
1276 * Fetch the default OID path.
1279 oid_get_default_mib_path(void) {
1285 path_str
= g_string_new("");
1287 if (!load_smi_modules
) {
1288 D(1,("OID resolution not enabled"));
1289 return g_string_free(path_str
, FALSE
);
1292 path
= get_datafile_path("snmp\\mibs");
1293 g_string_append_printf(path_str
, "%s;", path
);
1296 path
= get_persconffile_path("snmp\\mibs", false);
1297 g_string_append_printf(path_str
, "%s", path
);
1300 g_string_append(path_str
, "/usr/share/snmp/mibs");
1302 smiInit("wireshark");
1303 path
= smiGetPath();
1304 if (strlen(path
) > 0 ) {
1305 g_string_append(path_str
, G_SEARCHPATH_SEPARATOR_S
);
1306 g_string_append_printf(path_str
, "%s", path
);
1310 if (oids_init_done
== false)
1313 for (i
= 0; i
< num_smi_paths
; i
++) {
1314 if (!(smi_paths
[i
].name
&& *smi_paths
[i
].name
))
1317 g_string_append_printf(path_str
, G_SEARCHPATH_SEPARATOR_S
"%s", smi_paths
[i
].name
);
1322 return g_string_free(path_str
, FALSE
);
1323 #else /* HAVE_LIBSMI */
1324 return g_strdup("");
1329 char* oid_test_a2b(uint32_t num_subids
, uint32_t* subids
) {
1330 uint8_t* sub2enc
= NULL
;
1331 uint8_t* str2enc
= NULL
;
1332 uint32_t* enc2sub
= NULL
;
1335 char* sub2str
= oid_subid2string(NULL
, subids
, num_subids
);
1336 unsigned sub2enc_len
= oid_subid2encoded(NULL
, num_subids
, subids
,&sub2enc
);
1337 unsigned enc2sub_len
= oid_encoded2subid(NULL
, sub2enc
, sub2enc_len
, &enc2sub
);
1338 char* enc2str
= oid_encoded2string(NULL
, sub2enc
, sub2enc_len
);
1339 unsigned str2enc_len
= oid_string2encoded(NULL
, sub2str
,&str2enc
);
1340 unsigned str2sub_len
= oid_string2subid(sub2str
,&str2sub
);
1342 char* sub2enc_str
= bytes_to_str_punct(NULL
, sub2enc
, sub2enc_len
, ':');
1343 char* enc2sub_str
= enc2sub
? oid_subid2string(NULL
, enc2sub
,enc2sub_len
) : wmem_strdup(NULL
, "-");
1344 char* str2enc_str
= bytes_to_str_punct(NULL
, str2enc
, str2enc_len
, ':');
1345 char* str2sub_str
= str2sub
? oid_subid2string(NULL
, str2sub
,str2sub_len
) : wmem_strdup(NULL
, "-");
1347 ret
= wmem_strdup_printf(NULL
,
1348 "oid_subid2string=%s \n"
1349 "oid_subid2encoded=[%d]%s \n"
1350 "oid_encoded2subid=%s \n "
1351 "oid_encoded2string=%s \n"
1352 "oid_string2encoded=[%d]%s \n"
1353 "oid_string2subid=%s \n "
1355 ,sub2enc_len
,sub2enc_str
1358 ,str2enc_len
,str2enc_str
,
1362 wmem_free(NULL
, sub2enc_str
);
1363 wmem_free(NULL
, enc2sub_str
);
1364 wmem_free(NULL
, str2enc_str
);
1365 wmem_free(NULL
, str2sub_str
);
1367 wmem_free(NULL
, sub2str
);
1368 wmem_free(NULL
, enc2sub
);
1369 wmem_free(NULL
, sub2enc
);
1370 wmem_free(NULL
, str2enc
);
1371 wmem_free(NULL
, enc2str
);
1375 void add_oid_debug_subtree(oid_info_t
* oid_info
, proto_tree
*tree
) {
1376 static const char* oid_kinds
[] = { "Unknown", "Node", "Scalar", "Table", "Row", "Column", "Notification", "Group", "Compliance", "Capabilities"};
1377 static const char* key_types
[] = {"OID_KEY_TYPE_WRONG","OID_KEY_TYPE_INTEGER",
1378 "OID_KEY_TYPE_FIXED_STRING","OID_KEY_TYPE_FIXED_BYTES","OID_KEY_TYPE_STRING",
1379 "OID_KEY_TYPE_BYTES","OID_KEY_TYPE_NSAP","OID_KEY_TYPE_OID","OID_KEY_TYPE_IPADDR"};
1380 proto_item
* pi
= proto_tree_add_debug_text(tree
,NULL
,0,0,
1381 "OidInfo: Name='%s' sub-id=%u kind=%s hfid=%d",
1382 oid_info
->name
? oid_info
->name
: "",
1384 oid_info
->kind
<= OID_KIND_CAPABILITIES
? oid_kinds
[oid_info
->kind
] : "BROKEN",
1385 oid_info
->value_hfid
);
1386 proto_tree
* pt
= proto_item_add_subtree(pi
,0);
1389 for(key
= oid_info
->key
; key
; key
= key
->next
) {
1390 proto_tree_add_debug_text(pt
,NULL
,0,0,
1391 "Key: name='%s' num_subids=%d type=%s",
1393 key
->key_type
<= OID_KEY_TYPE_IPADDR
? key_types
[key
->key_type
] : "BROKEN"
1397 if (oid_info
->parent
) {
1398 pi
= proto_tree_add_debug_text(pt
,NULL
,0,0,"Parent:");
1399 pt
= proto_item_add_subtree(pi
,0);
1400 add_oid_debug_subtree(oid_info
->parent
, pt
);
1411 * indent-tabs-mode: t
1414 * ex: set shiftwidth=8 tabstop=8 noexpandtab:
1415 * :indentSize=8:tabSize=8:noTabs=false: