{ 0, NULL }
};
+extern value_string_ext linux_negative_errno_vals_ext;
+
static dissector_handle_t netlink_handle;
static header_field_info *hfi_netlink = NULL;
{ "Port ID", "netlink.hdr_pid", FT_UINT32, BASE_DEC,
NULL, 0x00, "Sender port ID", HFILL };
+static header_field_info hfi_netlink_attr_len NETLINK_HFI_INIT =
+ { "Len", "netlink.attr_len", FT_UINT16, BASE_DEC,
+ NULL, 0x00, NULL, HFILL };
+
static header_field_info hfi_netlink_attr_type NETLINK_HFI_INIT =
{ "Type", "netlink.attr_type", FT_UINT16, BASE_HEX,
NULL, 0x0000, "Netlink Attribute type", HFILL };
{ "Network byte order", "netlink.attr_type.net_byteorder", FT_UINT16, BASE_DEC,
NULL, NLA_F_NET_BYTEORDER, "Payload stored in network byte order", HFILL };
-static header_field_info hfi_netlink_attr_len NETLINK_HFI_INIT =
- { "Len", "netlink.attr_len", FT_UINT16, BASE_DEC,
+static header_field_info hfi_netlink_attr_index NETLINK_HFI_INIT =
+ { "Index", "netlink.attr_index", FT_UINT16, BASE_DEC,
+ NULL, 0x0000, "Netlink Attribute type (array index)", HFILL };
+
+static header_field_info hfi_netlink_attr_data NETLINK_HFI_INIT =
+ { "Data", "netlink.attr_data", FT_BYTES, BASE_NONE,
NULL, 0x00, NULL, HFILL };
/* TODO add a value_string for errno. */
static header_field_info hfi_netlink_error NETLINK_HFI_INIT =
- { "Error code", "netlink.error", FT_INT32, BASE_DEC,
- NULL, 0x00, "Negative errno or 0 for acknowledgements", HFILL };
+ { "Error code", "netlink.error", FT_INT32, BASE_DEC | BASE_EXT_STRING,
+ &linux_negative_errno_vals_ext, 0x00, "Negative errno or 0 for acknowledgements", HFILL };
static gint ett_netlink_cooked = -1;
static gint ett_netlink_msghdr = -1;
};
-int
-dissect_netlink_attributes(tvbuff_t *tvb, header_field_info *hfi_type, int ett, void *data, proto_tree *tree, int offset, int length, netlink_attributes_cb_t cb)
+static int
+dissect_netlink_attributes_common(tvbuff_t *tvb, header_field_info *hfi_type, int ett_tree, int ett_attrib, void *data, struct packet_netlink_data *nl_data, proto_tree *tree, int offset, int length, netlink_attributes_cb_t cb)
{
+ int encoding;
int padding = (4 - offset) & 3;
+ DISSECTOR_ASSERT(nl_data);
+
+ encoding = nl_data->encoding;
+
/* align to 4 */
offset += padding;
if (length == -1) {
proto_item *ti, *type_item;
proto_tree *attr_tree, *type_tree;
- rta_len = tvb_get_letohs(tvb, offset);
+ rta_len = tvb_get_guint16(tvb, offset, encoding);
if (rta_len < 4) {
/* XXX invalid expert */
break;
/* XXX expert info when rta_len < length? */
rta_len = MIN(rta_len, length);
- attr_tree = proto_tree_add_subtree(tree, tvb, offset, rta_len, ett, &ti, "Attribute");
-
- proto_tree_add_item(attr_tree, &hfi_netlink_attr_len, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- offset += 2;
+ attr_tree = proto_tree_add_subtree(tree, tvb, offset, rta_len, ett_tree, &ti, "Attribute");
- rta_type = tvb_get_letohs(tvb, offset);
- type = rta_type & NLA_TYPE_MASK;
- type_item = proto_tree_add_item(attr_tree, &hfi_netlink_attr_type, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- type_tree = proto_item_add_subtree(type_item, ett_netlink_attr_type);
- proto_tree_add_item(type_tree, &hfi_netlink_attr_type_nested, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- proto_tree_add_item(type_tree, &hfi_netlink_attr_type_net_byteorder, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- proto_tree_add_item(type_tree, hfi_type, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(attr_tree, &hfi_netlink_attr_len, tvb, offset, 2, encoding);
offset += 2;
- if (rta_type & NLA_F_NESTED)
- proto_item_append_text(type_item, ", Nested");
-
- if (hfi_type->strings) {
- /* XXX, export hf_try_val_to_str */
- const char *rta_str = try_val_to_str(type, (const value_string *) hfi_type->strings);
+ rta_type = tvb_get_guint16(tvb, offset, encoding);
+ if (ett_attrib == -1) {
+ /* List of attributes */
+ type = rta_type & NLA_TYPE_MASK;
+ type_item = proto_tree_add_item(attr_tree, &hfi_netlink_attr_type, tvb, offset, 2, encoding);
+ type_tree = proto_item_add_subtree(type_item, ett_netlink_attr_type);
+ proto_tree_add_item(type_tree, &hfi_netlink_attr_type_nested, tvb, offset, 2, encoding);
+ proto_tree_add_item(type_tree, &hfi_netlink_attr_type_net_byteorder, tvb, offset, 2, encoding);
+ proto_tree_add_item(type_tree, hfi_type, tvb, offset, 2, encoding);
+ offset += 2;
+
+ if (rta_type & NLA_F_NESTED)
+ proto_item_append_text(type_item, ", Nested");
+
+ if (hfi_type->strings) {
+ /* XXX, export hf_try_val_to_str */
+ const char *rta_str;
+
+ if (hfi_type->display & BASE_EXT_STRING) {
+ rta_str = try_val_to_str_ext(type, (value_string_ext *)hfi_type->strings);
+ } else {
+ rta_str = try_val_to_str(type, (const value_string *) hfi_type->strings);
+ }
+
+ if (rta_str) {
+ proto_item_append_text(type_item, ", %s (%d)", rta_str, type);
+ proto_item_append_text(ti, ": %s", rta_str);
+ }
+ }
- if (rta_str) {
- proto_item_append_text(type_item, ", %s (%d)", rta_str, type);
- proto_item_append_text(ti, ": %s", rta_str);
+ if (!cb(tvb, data, attr_tree, rta_type, offset, rta_len - 4)) {
+ proto_tree_add_item(attr_tree, &hfi_netlink_attr_data, tvb, offset, rta_len - 4, encoding);
}
- }
+ } else {
+ /*
+ * Nested attributes, constructing an array (list of
+ * attributes where its type is the array index and its
+ * value is the actual list of interesting attributes).
+ */
+ proto_tree_add_item(attr_tree, &hfi_netlink_attr_index, tvb, offset, 2, encoding);
+ offset += 2;
+ proto_item_append_text(ti, " %u", rta_type);
- if (!cb(tvb, data, attr_tree, rta_type, offset, rta_len - 4)) {
- /* not handled */
+ dissect_netlink_attributes(tvb, hfi_type, ett_attrib, data, nl_data, attr_tree, offset, rta_len - 4, cb);
}
/* Assume offset already aligned, next offset is rta_len plus alignment. */
return offset;
}
-static int
-dissect_netlink_hdr(tvbuff_t *tvb, proto_tree *tree, int offset, int encoding, guint16 *type, guint32 *port_id)
+int
+dissect_netlink_attributes(tvbuff_t *tvb, header_field_info *hfi_type, int ett, void *data, struct packet_netlink_data *nl_data, proto_tree *tree, int offset, int length, netlink_attributes_cb_t cb)
+{
+ return dissect_netlink_attributes_common(tvb, hfi_type, ett, -1, data, nl_data, tree, offset, length, cb);
+}
+
+int
+dissect_netlink_attributes_array(tvbuff_t *tvb, header_field_info *hfi_type, int ett_array, int ett_attrib, void *data, struct packet_netlink_data *nl_data, proto_tree *tree, int offset, int length, netlink_attributes_cb_t cb)
+{
+ DISSECTOR_ASSERT(ett_attrib != -1);
+ return dissect_netlink_attributes_common(tvb, hfi_type, ett_array, ett_attrib, data, nl_data, tree, offset, length, cb);
+}
+
+int
+dissect_netlink_header(tvbuff_t *tvb, proto_tree *tree, int offset, int encoding, header_field_info *hfi_type, proto_item **pi_type)
{
guint16 hdr_flags;
guint16 hdr_type;
-
proto_tree *fh_hdr;
- proto_item *pi_type;
+ proto_item *pi;
- fh_hdr = proto_tree_add_subtree(tree, tvb, offset, 16, ett_netlink_msghdr, NULL, "Header");
+ fh_hdr = proto_tree_add_subtree(tree, tvb, offset, 16, ett_netlink_msghdr, NULL, "Netlink message header");
proto_tree_add_item(fh_hdr, &hfi_netlink_hdr_len, tvb, offset, 4, encoding);
offset += 4;
- pi_type = proto_tree_add_item(fh_hdr, &hfi_netlink_hdr_type, tvb, offset, 2, encoding);
- hdr_type = tvb_get_letohs(tvb, offset);
- if (hdr_type >= WS_NLMSG_MIN_TYPE) {
- proto_item_set_text(pi_type, "Message type: Protocol-specific (0x%04x)", hdr_type);
+ hdr_type = tvb_get_guint16(tvb, offset, encoding);
+ if (hdr_type < WS_NLMSG_MIN_TYPE) {
+ /* Reserved control messages. */
+ hfi_type = &hfi_netlink_hdr_type;
+ pi = proto_tree_add_item(fh_hdr, hfi_type, tvb, offset, 2, encoding);
+ } else {
+ if (hfi_type) {
+ pi = proto_tree_add_item(fh_hdr, hfi_type, tvb, offset, 2, encoding);
+ } else {
+ hfi_type = &hfi_netlink_hdr_type;
+ pi = proto_tree_add_item(fh_hdr, hfi_type, tvb, offset, 2, encoding);
+ proto_item_set_text(pi, "Message type: Protocol-specific (0x%04x)", hdr_type);
+ }
+ }
+ if (pi_type) {
+ *pi_type = pi;
}
- if (type) {
- *type = hdr_type;
+ /* TODO export hf_try_val_to_str? */
+ if (hfi_type->strings && hfi_type->display & BASE_EXT_STRING) {
+ proto_item_append_text(fh_hdr, " (type: %s)", val_to_str_ext(hdr_type, (value_string_ext *)hfi_type->strings, "0x%04x"));
+ } else if (hfi_type->strings) {
+ proto_item_append_text(fh_hdr, " (type: %s)", val_to_str(hdr_type, (const value_string *)hfi_type->strings, "0x%04x"));
+ } else {
+ proto_item_append_text(fh_hdr, " (type: 0x%04x)", hdr_type);
}
offset += 2;
proto_tree_add_item(fh_hdr, &hfi_netlink_hdr_seq, tvb, offset, 4, encoding);
offset += 4;
- proto_tree_add_item_ret_uint(fh_hdr, hfi_netlink_hdr_pid.id, tvb, offset, 4, encoding, port_id);
+ proto_tree_add_item(fh_hdr, &hfi_netlink_hdr_pid, tvb, offset, 4, encoding);
offset += 4;
return offset;
static void
dissect_netlink_error(tvbuff_t *tvb, proto_tree *tree, int offset, int encoding)
{
- /* Assume sizeof(int) == 4. */
+ /*
+ * XXX - this should make sure we don't run past the end of the
+ * message.
+ */
+
+ /*
+ * Assume sizeof(int) == 4; RFC 3549 doesn't say "32 bits", it
+ * says "integer (typically 32 bits)".
+ */
proto_tree_add_item(tree, &hfi_netlink_error, tvb, offset, 4, encoding);
offset += 4;
- dissect_netlink_hdr(tvb, tree, offset, encoding, NULL, NULL);
+ dissect_netlink_header(tvb, tree, offset, encoding, NULL, NULL);
}
static int
proto_tree *fh_tree;
int offset;
+ int encoding;
+ guint len_rem, len_le, len_be;
hatype = tvb_get_ntohs(tvb, 2);
if (hatype != ARPHRD_NETLINK)
/* DISSECTOR_ASSERT(offset == 16); */
- while (tvb_reported_length_remaining(tvb, offset) >= 16) {
- struct packet_netlink_data data;
+ /*
+ * We are unable to detect the endianness, we have to guess. Compare
+ * the size of the inner package with the size of the outer package,
+ * take the endianness in which the inner package length is closer to
+ * the size of the outer package. Normally we have packages with less
+ * than 10KB here so the sizes are very huge in the wrong endianness.
+ */
+ len_rem = tvb_reported_length_remaining(tvb, offset);
+ len_le = tvb_get_letohl(tvb, offset);
+ len_be = tvb_get_ntohl(tvb, offset);
+ #define abs_diff(a, b) ((a) > (b) ? (a) - (b) : (b) - (a))
+ if (abs_diff(len_be, len_rem) < abs_diff(len_le, len_rem)) {
+ encoding = ENC_BIG_ENDIAN;
+ } else {
+ encoding = ENC_LITTLE_ENDIAN;
+ }
+ while (tvb_reported_length_remaining(tvb, offset) >= 16) {
int pkt_end_offset;
+ guint16 msg_type;
guint32 pkt_len;
guint32 port_id;
-
proto_tree *fh_msg;
+ gboolean dissected = FALSE;
-
- int encoding = ENC_LITTLE_ENDIAN; /* XXX */
-
- pkt_len = tvb_get_letohl(tvb, offset);
+ pkt_len = tvb_get_guint32(tvb, offset, encoding);
pkt_end_offset = offset + pkt_len;
- fh_msg = proto_tree_add_subtree(tree, tvb, offset, pkt_len, ett_netlink_msg, NULL, "Netlink message");
-
if (pkt_len < 16) {
/*
* This field includes the length of the 16-byte header,
*/
proto_tree *fh_hdr;
- fh_hdr = proto_tree_add_subtree(tree, tvb, offset, 4, ett_netlink_msghdr, NULL, "Header");
+ fh_hdr = proto_tree_add_subtree(tree, tvb, offset, 4, ett_netlink_msghdr, NULL, "Netlink message header");
proto_tree_add_item(fh_hdr, &hfi_netlink_hdr_len, tvb, offset, 4, encoding);
/* XXX invalid expert */
break;
}
-
- offset = dissect_netlink_hdr(tvb, fh_msg, offset, encoding, &data.type, &port_id);
+
+ /* message type field comes after length field. */
+ msg_type = tvb_get_guint16(tvb, offset + 4, encoding);
+ port_id = tvb_get_guint32(tvb, offset + 12, encoding);
/* XXX */
if (port_id == 0x00)
else
pinfo->p2p_dir = P2P_DIR_RECV; /* userspace or kernel -> userspace */
- if (data.type == WS_NLMSG_ERROR) {
- dissect_netlink_error(tvb, fh_msg, offset, encoding);
- } else if (pkt_len > 16) {
+ /*
+ * Try to invoke subdissectors for non-control messages.
+ */
+ if (msg_type >= WS_NLMSG_MIN_TYPE && pkt_len > 16) {
+ struct packet_netlink_data data;
+
data.magic = PACKET_NETLINK_MAGIC;
data.encoding = encoding;
+ data.type = msg_type;
- next_tvb = tvb_new_subset_length(tvb, offset, pkt_len-16);
+ next_tvb = tvb_new_subset_length(tvb, offset, pkt_len);
- if (!dissector_try_uint_new(netlink_dissector_table, protocol, next_tvb, pinfo, fh_msg, TRUE, &data))
- call_data_dissector(next_tvb, pinfo, fh_msg);
+ if (dissector_try_uint_new(netlink_dissector_table, protocol, next_tvb, pinfo, tree, TRUE, &data)) {
+ dissected = TRUE;
+ }
+ }
- } else if (pkt_len != 16) {
- /* XXX, expert info */
- break;
+ if (!dissected) {
+ /*
+ * No subdissector was called, add a new layer with the
+ * header and the payload. Note that pkt_len>=16.
+ */
+ fh_msg = proto_tree_add_subtree(tree, tvb, offset, pkt_len, ett_netlink_msg, NULL, "Netlink message");
+ offset = dissect_netlink_header(tvb, fh_msg, offset, encoding, NULL, NULL);
+
+ if (msg_type == WS_NLMSG_ERROR) {
+ dissect_netlink_error(tvb, fh_msg, offset, encoding);
+ } else if (pkt_len > 16) {
+ next_tvb = tvb_new_subset_length(tvb, offset, pkt_len - 16);
+ call_data_dissector(next_tvb, pinfo, fh_msg);
+ }
}
offset = pkt_end_offset;
&hfi_netlink_attr_type,
&hfi_netlink_attr_type_nested,
&hfi_netlink_attr_type_net_byteorder,
+ &hfi_netlink_attr_index,
+ &hfi_netlink_attr_data,
/* Netlink message payloads */
&hfi_netlink_error,
"netlink.protocol",
"Linux netlink protocol type",
proto_netlink, FT_UINT16,
- BASE_HEX, DISSECTOR_TABLE_NOT_ALLOW_DUPLICATE
+ BASE_HEX
);
register_dissector("netlink", dissect_netlink, proto_netlink);
}