summaryrefslogtreecommitdiff
path: root/epan/dissectors/packet-epl-profile-parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-epl-profile-parser.c')
-rw-r--r--epan/dissectors/packet-epl-profile-parser.c548
1 files changed, 535 insertions, 13 deletions
diff --git a/epan/dissectors/packet-epl-profile-parser.c b/epan/dissectors/packet-epl-profile-parser.c
index 771a395009..7ac6d2a1e3 100644
--- a/epan/dissectors/packet-epl-profile-parser.c
+++ b/epan/dissectors/packet-epl-profile-parser.c
@@ -39,12 +39,100 @@
#include <string.h>
#include <stdlib.h>
+#include <wsutil/strtoi.h>
+#include <wsutil/str_util.h>
#include <epan/wmem/wmem.h>
-/* XXX: Temporary. successive related change makes use of the functions here */
-#ifdef __GNUC__
-#pragma GCC diagnostic ignored "-Wunused-function"
-#endif
+#if defined HAVE_LIBXML2
+#include <libxml/xmlversion.h>
+
+#if defined LIBXML_XPATH_ENABLED \
+&& defined LIBXML_SAX1_ENABLED \
+&& defined LIBXML_TREE_ENABLED
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#define PARSE_XDD 1
+
+typedef int xpath_handler(xmlNodeSetPtr, void*);
+static xpath_handler populate_object_list, populate_datatype_list, populate_profile_name;
+
+static struct xpath_namespace {
+ const xmlChar *prefix, *href;
+} namespaces[] = {
+ { BAD_CAST "x", BAD_CAST "http://www.ethernet-powerlink.org" },
+ { BAD_CAST "xsi", BAD_CAST "http://www.w3.org/2001/XMLSchema-instance" },
+ { NULL, NULL }
+};
+
+static struct xpath {
+ const xmlChar *expr;
+ xpath_handler *handler;
+} xpaths[] = {
+ {
+ BAD_CAST "//x:ISO15745Profile[x:ProfileHeader/x:ProfileIdentification='Powerlink_Communication_Profile']/x:ProfileHeader/x:ProfileName",
+ populate_profile_name
+ },
+ {
+ BAD_CAST "//x:ProfileBody[@xsi:type='ProfileBody_CommunicationNetwork_Powerlink']/x:ApplicationLayers/x:DataTypeList/x:defType",
+ populate_datatype_list
+ },
+ {
+ BAD_CAST "//x:ProfileBody[@xsi:type='ProfileBody_CommunicationNetwork_Powerlink']/x:ApplicationLayers/x:ObjectList/x:Object",
+ populate_object_list
+ },
+ { NULL, NULL }
+};
+
+
+#endif /* LIBXML_XPATH_ENABLED && LIBXML_SAX1_ENABLED && LIBXML_TREE_ENABLED */
+
+#endif /* HAVE_LIBXML2 */
+
+struct datatype {
+ guint16 id;
+ const struct epl_datatype *ptr;
+};
+
+static struct typemap_entry {
+ guint16 id;
+ const char *name;
+ struct epl_datatype *type;
+} epl_datatypes[] = {
+ {0x0001, "Boolean", NULL},
+ {0x0002, "Integer8", NULL},
+ {0x0003, "Integer16", NULL},
+ {0x0004, "Integer32", NULL},
+ {0x0005, "Unsigned8", NULL},
+ {0x0006, "Unsigned16", NULL},
+ {0x0007, "Unsigned32", NULL},
+ {0x0008, "Real32", NULL},
+ {0x0009, "Visible_String", NULL},
+ {0x0010, "Integer24", NULL},
+ {0x0011, "Real64", NULL},
+ {0x0012, "Integer40", NULL},
+ {0x0013, "Integer48", NULL},
+ {0x0014, "Integer56", NULL},
+ {0x0015, "Integer64", NULL},
+ {0x000A, "Octet_String", NULL},
+ {0x000B, "Unicode_String", NULL},
+ {0x000C, "Time_of_Day", NULL},
+ {0x000D, "Time_Diff", NULL},
+ {0x000F, "Domain", NULL},
+ {0x0016, "Unsigned24", NULL},
+ {0x0018, "Unsigned40", NULL},
+ {0x0019, "Unsigned48", NULL},
+ {0x001A, "Unsigned56", NULL},
+ {0x001B, "Unsigned64", NULL},
+ {0x0401, "MAC_ADDRESS", NULL},
+ {0x0402, "IP_ADDRESS", NULL},
+ {0x0403, "NETTIME", NULL},
+ {0x0000, NULL, NULL}
+};
+
+static wmem_map_t *eds_typemap;
struct epl_wmem_iarray {
GEqualFunc equal;
@@ -55,12 +143,446 @@ struct epl_wmem_iarray {
};
static epl_wmem_iarray_t *epl_wmem_iarray_new(wmem_allocator_t *allocator, const guint elem_size, GEqualFunc cmp) G_GNUC_MALLOC;
-static gboolean epl_wmem_iarray_is_empty(epl_wmem_iarray_t *iarr);
-static gboolean epl_wmem_iarray_is_sorted(epl_wmem_iarray_t *iarr);
+gboolean epl_wmem_iarray_is_empty(epl_wmem_iarray_t *iarr);
+gboolean epl_wmem_iarray_is_sorted(epl_wmem_iarray_t *iarr);
static void epl_wmem_iarray_insert(epl_wmem_iarray_t *iarr, guint32 where, range_admin_t *data);
static void epl_wmem_iarray_sort_and_compact(epl_wmem_iarray_t *iarr);
-static range_admin_t * epl_wmem_iarray_find(epl_wmem_iarray_t *arr, guint32 value);
+static gboolean
+epl_ishex(const char *num)
+{
+ if (g_str_has_prefix(num, "0x"))
+ return TRUE;
+
+ for (; g_ascii_isxdigit(*num); num++)
+ ;
+
+ if (g_ascii_tolower(*num) == 'h')
+ return TRUE;
+
+ return FALSE;
+}
+
+static guint16
+epl_g_key_file_get_uint16(GKeyFile *gkf, const gchar *group_name, const gchar *key, GError **error)
+{
+ guint16 ret = 0;
+ const char *endptr;
+ char *val = g_key_file_get_string(gkf, group_name, key, error);
+
+ if (!val)
+ return 0;
+
+ if (epl_ishex(val)) /* We need to support XXh, but no octals (is that right?) */
+ ws_hexstrtou16(val, &endptr, &ret);
+ else
+ ws_strtou16(val, &endptr, &ret);
+
+ g_free(val);
+ return ret;
+}
+
+static void
+sort_subindices(void *key _U_, void *value, void *user_data _U_)
+{
+ epl_wmem_iarray_t *subindices = ((struct object*)value)->subindices;
+ if (subindices)
+ epl_wmem_iarray_sort_and_compact(subindices);
+}
+
+void
+epl_eds_init(void)
+{
+ struct typemap_entry *entry;
+ eds_typemap = wmem_map_new(wmem_epan_scope(), g_direct_hash, g_direct_equal);
+ for (entry = epl_datatypes; entry->name; entry++)
+ {
+ const struct epl_datatype *type = epl_type_to_hf(entry->name);
+ wmem_map_insert(eds_typemap, GUINT_TO_POINTER(entry->id), (void*)type);
+ }
+}
+
+struct profile *
+epl_eds_load(struct profile *profile, const char *eds_file)
+{
+ GKeyFile* gkf;
+ GError *err;
+ char **group, **groups;
+ char *val;
+ gsize groups_count;
+
+ gkf = g_key_file_new();
+
+ /* Load EDS document */
+ if (!g_key_file_load_from_file(gkf, eds_file, G_KEY_FILE_NONE, &err)){
+ g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to parse file \"%s\"\n", eds_file);
+ profile = NULL;
+ goto cleanup;
+ }
+
+ profile->path = wmem_strdup(profile->scope, eds_file);
+
+ val = g_key_file_get_string(gkf, "FileInfo", "Description", NULL);
+ /* This leaves a trailing space, but that's ok */
+ profile->name = wmem_strndup(profile->scope, val, strcspn(val, "#"));
+ g_free(val);
+
+ groups = g_key_file_get_groups(gkf, &groups_count);
+ for (group = groups; *group; group++)
+ {
+ char *name;
+ const char *endptr;
+ guint16 idx = 0, datatype;
+ struct object *obj = NULL;
+ struct od_entry tmpobj = {0};
+ gboolean is_object = TRUE;
+
+ if (!g_ascii_isxdigit(**group))
+ continue;
+
+ ws_hexstrtou16(*group, &endptr, &idx);
+ if (*endptr == '\0')
+ { /* index */
+ tmpobj.idx = idx;
+ }
+ else if (g_str_has_prefix(endptr, "sub"))
+ { /* subindex */
+ if (!ws_hexstrtou16(endptr + 3, &endptr, &tmpobj.idx)
+ || tmpobj.idx > 0xFF)
+ continue;
+
+ is_object = FALSE;
+ }
+ else continue;
+
+ tmpobj.type_class = epl_g_key_file_get_uint16(gkf, *group, "ObjectType", NULL);
+ if (!tmpobj.type_class)
+ continue;
+
+ datatype = epl_g_key_file_get_uint16(gkf, *group, "DataType", NULL);
+ if (datatype)
+ tmpobj.type = (const struct epl_datatype*)wmem_map_lookup(eds_typemap, GUINT_TO_POINTER(datatype));
+
+ if ((name = g_key_file_get_string(gkf, *group, "ParameterName", NULL)))
+ {
+ gsize count = strcspn(name, "#") + 1;
+ g_strlcpy(
+ tmpobj.name,
+ name,
+ count > sizeof tmpobj.name ? sizeof tmpobj.name : count
+ );
+ g_free(name);
+ }
+
+ obj = epl_profile_object_lookup_or_add(profile, idx);
+
+ if (is_object)
+ { /* Let's add a new object! Exciting! */
+ obj->info = tmpobj;
+ }
+ else
+ { /* Object already there, let's add subindices */
+ struct subobject subobj = {0};
+ if (!obj->subindices)
+ {
+ obj->subindices = epl_wmem_iarray_new(
+ profile->scope,
+ sizeof (struct subobject),
+ subobject_equal
+ );
+ }
+
+ subobj.info = tmpobj;
+ epl_wmem_iarray_insert(obj->subindices, subobj.info.idx, &subobj.range);
+ }
+ }
+
+ /* Unlike with XDDs, subindices might interleave with others, so let's sort them now */
+ wmem_map_foreach(profile->objects, sort_subindices, NULL);
+
+ /* We don't read object mappings from EDS files */
+ /* epl_profile_object_mappings_update(profile); */
+
+cleanup:
+ g_key_file_free(gkf);
+ return profile;
+}
+
+#ifdef PARSE_XDD
+
+void
+epl_xdd_init(void)
+{
+}
+
+struct profile *
+epl_xdd_load(struct profile *profile, const char *xml_file)
+{
+ xmlXPathContextPtr xpathCtx = NULL;
+ xmlDoc *doc = NULL;
+ struct xpath_namespace *ns = NULL;
+ struct xpath *xpath = NULL;
+ GHashTable *typemap = NULL;
+
+ /* Load XML document */
+ doc = xmlParseFile(xml_file);
+ if (!doc)
+ {
+ g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to parse file \"%s\"\n", xml_file);
+ profile = NULL;
+ goto cleanup;
+ }
+
+
+ /* Create xpath evaluation context */
+ xpathCtx = xmlXPathNewContext(doc);
+ if(!xpathCtx)
+ {
+ g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to create new XPath context\n");
+ profile = NULL;
+ goto cleanup;
+ }
+
+ /* Register namespaces from list */
+ for (ns = namespaces; ns->href; ns++)
+ {
+ if(xmlXPathRegisterNs(xpathCtx, ns->prefix, ns->href) != 0)
+ {
+ g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href);
+ profile = NULL;
+ goto cleanup;
+ }
+ }
+
+ profile->path = wmem_strdup(profile->scope, xml_file);
+
+ /* mapping type ids to &hf_s */
+ profile->data = typemap = (GHashTable*)g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+
+ /* Evaluate xpath expressions */
+ for (xpath = xpaths; xpath->expr; xpath++)
+ {
+ xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(xpath->expr, xpathCtx);
+ if (!xpathObj || !xpathObj->nodesetval)
+ {
+ g_log(NULL, G_LOG_LEVEL_WARNING, "Error: unable to evaluate xpath expression \"%s\"\n", xpath->expr);
+ xmlXPathFreeObject(xpathObj);
+ profile = NULL;
+ goto cleanup;
+ }
+
+ /* run handler */
+ if (xpath->handler && xpathObj->nodesetval->nodeNr)
+ xpath->handler(xpathObj->nodesetval, profile);
+ xmlXPathFreeObject(xpathObj);
+ }
+
+ /* We create ObjectMappings while reading the XML, this is makes it likely,
+ * that we won't be able to reference a mapped object in the ObjectMapping
+ * as we didn't reach its XML tag yet. Therefore, after reading the XDD
+ * completely, we update mappings in the profile
+ */
+ epl_profile_object_mappings_update(profile);
+
+cleanup:
+ if (typemap)
+ g_hash_table_destroy(typemap);
+
+ if (xpathCtx)
+ xmlXPathFreeContext(xpathCtx);
+ if (doc)
+ xmlFreeDoc(doc);
+
+ return profile;
+}
+
+static int
+populate_profile_name(xmlNodeSetPtr nodes, void *_profile)
+{
+ struct profile *profile = (struct profile*)_profile;
+ if (nodes->nodeNr == 1
+ && nodes->nodeTab[0]->type == XML_ELEMENT_NODE
+ && nodes->nodeTab[0]->children)
+ {
+ profile->name = wmem_strdup(profile->scope, (char*)nodes->nodeTab[0]->children->content);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+populate_datatype_list(xmlNodeSetPtr nodes, void *_profile)
+{
+ xmlNodePtr cur;
+ int i;
+ struct profile *profile = (struct profile*)_profile;
+
+ for(i = 0; i < nodes->nodeNr; ++i)
+ {
+ xmlAttrPtr attr;
+
+ if(!nodes->nodeTab[i] || nodes->nodeTab[i]->type != XML_ELEMENT_NODE)
+ return -1;
+
+ cur = nodes->nodeTab[i];
+
+
+ for(attr = cur->properties; attr; attr = attr->next)
+ {
+ const char *endptr;
+ const char *key = (const char*)attr->name;
+ const char *val = (const char*)attr->children->content;
+
+ if (g_str_equal("dataType", key))
+ {
+ xmlNode *subnode;
+ guint16 idx = 0;
+
+ if (!ws_hexstrtou16(val, &endptr, &idx))
+ continue;
+
+ for (subnode = cur->children; subnode; subnode = subnode->next)
+ {
+ if (subnode->type == XML_ELEMENT_NODE)
+ {
+ struct datatype *type;
+ const struct epl_datatype *ptr = epl_type_to_hf((char*)subnode->name);
+ if (!ptr)
+ {
+ g_log(NULL, G_LOG_LEVEL_INFO, "Skipping unknown type '%s'\n", subnode->name);
+ continue;
+ }
+ type = g_new(struct datatype, 1);
+ type->id = idx;
+ type->ptr = ptr;
+ g_hash_table_insert((GHashTable*)profile->data, GUINT_TO_POINTER(type->id), type);
+ continue;
+ }
+ }
+
+ }
+ }
+ }
+
+ return 0;
+}
+
+static gboolean
+parse_obj_tag(xmlNode *cur, struct od_entry *out, struct profile *profile) {
+ xmlAttrPtr attr;
+ const char *defaultValue = NULL, *actualValue = NULL;
+ const char *endptr;
+
+ for(attr = cur->properties; attr; attr = attr->next)
+ {
+ const char *key = (char*)attr->name,
+ *val = (char*)attr->children->content;
+
+ if (g_str_equal("index", key))
+ {
+ if (!ws_hexstrtou16(val, &endptr, &out->idx))
+ return FALSE;
+
+ } else if (g_str_equal("subIndex", key)) {
+ if (!ws_hexstrtou16(val, &endptr, &out->idx))
+ return FALSE;
+
+ } else if (g_str_equal("name", key)) {
+ g_strlcpy(out->name, val, sizeof out->name);
+
+ } else if (g_str_equal("objectType", key)) {
+ out->type_class = 0;
+ ws_hexstrtou16(val, &endptr, &out->type_class);
+
+ } else if (g_str_equal("dataType", key)) {
+ guint16 id;
+ if (ws_hexstrtou16(val, &endptr, &id))
+ {
+ struct datatype *type = (struct datatype*)g_hash_table_lookup((GHashTable*)profile->data, GUINT_TO_POINTER(id));
+ if (type) out->type = type->ptr;
+ }
+
+ } else if (g_str_equal("defaultValue", key)) {
+ defaultValue = val;
+
+ } else if (g_str_equal("actualValue", key)) {
+ actualValue = val;
+ }
+#if 0
+ else if (g_str_equal("PDOmapping", key)) {
+ obj.PDOmapping = get_index(ObjectPDOmapping_tostr, val);
+ assert(obj.PDOmapping >= 0);
+ }
+#endif
+ }
+
+ if (actualValue)
+ out->value = g_ascii_strtoull(actualValue, NULL, 0);
+ else if (defaultValue)
+ out->value = g_ascii_strtoull(defaultValue, NULL, 0);
+ else
+ out->value = 0;
+
+
+ return TRUE;
+}
+
+static int
+populate_object_list(xmlNodeSetPtr nodes, void *_profile)
+{
+ int i;
+ struct profile *profile = (struct profile*)_profile;
+
+ for(i = 0; i < nodes->nodeNr; ++i)
+ {
+ xmlNodePtr cur = nodes->nodeTab[i];
+ struct od_entry tmpobj = {0};
+
+ if (!nodes->nodeTab[i] || nodes->nodeTab[i]->type != XML_ELEMENT_NODE)
+ continue;
+
+ parse_obj_tag(cur, &tmpobj, profile);
+
+ if (tmpobj.idx)
+ {
+ struct object *obj = epl_profile_object_add(profile, tmpobj.idx);
+ obj->info = tmpobj;
+
+ if (tmpobj.type_class == OD_ENTRY_ARRAY || tmpobj.type_class == OD_ENTRY_RECORD)
+ {
+ xmlNode *subcur;
+ struct subobject subobj = {0};
+
+ obj->subindices = epl_wmem_iarray_new(profile->scope, sizeof (struct subobject), subobject_equal);
+
+ for (subcur = cur->children; subcur; subcur = subcur->next)
+ {
+ if (subcur->type != XML_ELEMENT_NODE)
+ continue;
+
+ if (parse_obj_tag(subcur, &subobj.info, profile))
+ {
+ epl_wmem_iarray_insert(obj->subindices,
+ subobj.info.idx, &subobj.range);
+ }
+ if (subobj.info.value && epl_profile_object_mapping_add(
+ profile, obj->info.idx, (guint8)subobj.info.idx, subobj.info.value))
+ {
+ g_log(NULL, G_LOG_LEVEL_INFO,
+ "Loaded mapping from XDC %s:%s", obj->info.name, subobj.info.name);
+ }
+ }
+ epl_wmem_iarray_sort_and_compact(obj->subindices);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+#endif /* PARSE_XDD */
/**
* A sorted array keyed by intervals
@@ -104,7 +626,7 @@ free_garray(wmem_allocator_t *scope _U_, wmem_cb_event_t event _U_, void *data)
* it's always the second argument that's getting removed.
*/
-epl_wmem_iarray_t *
+static epl_wmem_iarray_t *
epl_wmem_iarray_new(wmem_allocator_t *scope, const guint elem_size, GEqualFunc equal)
{
epl_wmem_iarray_t *iarr;
@@ -126,21 +648,21 @@ epl_wmem_iarray_new(wmem_allocator_t *scope, const guint elem_size, GEqualFunc e
/** Returns true if the iarr is empty. */
-static gboolean
+gboolean
epl_wmem_iarray_is_empty(epl_wmem_iarray_t *iarr)
{
return iarr->arr->len == 0;
}
/** Returns true if the iarr is sorted. */
-static gboolean
+gboolean
epl_wmem_iarray_is_sorted(epl_wmem_iarray_t *iarr)
{
return iarr->is_sorted;
}
/** Inserts an element */
-void
+static void
epl_wmem_iarray_insert(epl_wmem_iarray_t *iarr, guint32 where, range_admin_t *data)
{
if (iarr->arr->len)
@@ -157,7 +679,7 @@ epl_wmem_iarray_cmp(const void *a, const void *b)
}
/** Makes array suitable for searching */
-void
+static void
epl_wmem_iarray_sort_and_compact(epl_wmem_iarray_t *iarr)
{
range_admin_t *elem, *prev = NULL;
@@ -207,7 +729,7 @@ bsearch_garray(const void *key, GArray *arr, int (*cmp)(const void*, const void*
* Finds an element in the interval array. Returns NULL if it doesn't exist
* Calling this is unspecified if the array wasn't sorted before
*/
-static range_admin_t *
+range_admin_t *
epl_wmem_iarray_find(epl_wmem_iarray_t *iarr, guint32 value) {
epl_wmem_iarray_sort_and_compact(iarr);