netlink: let subdissectors handle the netlink header

Let subdissectors handle parsing and addition of the Netlink header
instead of doing this before calling subdissectors. After this patch:

 - Protocol filters like "netlink-netfilter" can be used to match
   packets (previously only a text item was added to the tree).
 - Subdissectors have more freedom in modifying the type field, so now
   it shows the correct type directly rather than "Message type:
   Protocol-specific".
 - netfilter: the type fields are now actually linked to a tvb,
   previously it was linked to a NULL tvb.
 - netfilter: fix unintended rejection of packets (the length should
   have been added to the offset, otherwise it would fallback to the
   data dissector).
 - Now subdissectors will not be called for control messages (so the
   netlink-conntrack.pcap sample now shows "Netlink" instead of "Netlink
   route" for the "End of dump" control message).

Change-Id: I2ab1bef91fb0080664195b281a6a45c9702914e5
Reviewed-on: https://code.wireshark.org/review/20910
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Michael Mann <mmann78@netscape.net>
This commit is contained in:
Peter Wu 2017-04-04 21:30:45 +02:00 committed by Michael Mann
parent 0add542dbf
commit 92ebd63892
5 changed files with 121 additions and 88 deletions

View File

@ -239,6 +239,8 @@ enum ws_ipset_ip_attr {
};
static int proto_netlink_netfilter;
static int ett_netlink_netfilter = -1;
static int ett_nfq_config_attr = -1;
static int ett_nfq_attr = -1;
@ -735,11 +737,14 @@ static int
dissect_netfilter_ulog(tvbuff_t *tvb, netlink_netfilter_info_t *info, proto_tree *tree, int offset)
{
enum ws_nfulnl_msg_types type = (enum ws_nfulnl_msg_types) (info->data->type & 0xff);
tvbuff_t *next_tvb;
switch (type) {
case WS_NFULNL_MSG_PACKET:
/* Note that NFLOG dissects the nfgenmsg header */
call_dissector(nflog_handle, tvb, info->pinfo, tree);
next_tvb = tvb_new_subset_remaining(tvb, offset);
call_dissector(nflog_handle, next_tvb, info->pinfo, tree);
offset = tvb_reported_length(tvb);
break;
default:
@ -1117,35 +1122,34 @@ static header_field_info hfi_netlink_netfilter_subsys NETLINK_NETFILTER_HFI_INIT
static int
dissect_netlink_netfilter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *_data)
{
struct packet_netlink_data *data = NULL;
struct packet_netlink_data *data = (struct packet_netlink_data *)_data;
netlink_netfilter_info_t info;
int offset;
proto_tree *nlmsg_tree;
proto_item *pi;
int offset = 0;
if (_data) {
if (((struct packet_netlink_data *) _data)->magic == PACKET_NETLINK_MAGIC)
data = (struct packet_netlink_data *) _data;
}
DISSECTOR_ASSERT(data);
DISSECTOR_ASSERT(data && data->magic == PACKET_NETLINK_MAGIC);
col_set_str(pinfo->cinfo, COL_PROTOCOL, "Netlink netfilter");
col_clear(pinfo->cinfo, COL_INFO);
proto_item_set_text(tree, "Linux netlink netfilter message");
pi = proto_tree_add_item(tree, proto_registrar_get_nth(proto_netlink_netfilter), tvb, 0, -1, ENC_NA);
nlmsg_tree = proto_item_add_subtree(pi, ett_netlink_netfilter);
/* XXX, from header tvb */
proto_tree_add_uint(tree, &hfi_netlink_netfilter_subsys, NULL, 0, 0, data->type);
/* Netlink message header (nlmsghdr) */
offset = dissect_netlink_header(tvb, nlmsg_tree, offset, data->encoding, NULL, NULL);
proto_tree_add_item(nlmsg_tree, &hfi_netlink_netfilter_subsys, tvb, 4, 2, data->encoding);
switch (data->type >> 8) {
case WS_NFNL_SUBSYS_QUEUE:
proto_tree_add_uint(tree, &hfi_nfq_type, NULL, 0, 0, data->type);
proto_tree_add_item(nlmsg_tree, &hfi_nfq_type, tvb, 4, 2, data->encoding);
break;
case WS_NFNL_SUBSYS_ULOG:
proto_tree_add_uint(tree, &hfi_netlink_netfilter_ulog_type, NULL, 0, 0, data->type);
proto_tree_add_item(nlmsg_tree, &hfi_netlink_netfilter_ulog_type, tvb, 4, 2, data->encoding);
break;
case WS_NFNL_SUBSYS_IPSET:
proto_tree_add_uint(tree, &hfi_ipset_command, NULL, 0, 0, data->type);
proto_tree_add_item(nlmsg_tree, &hfi_ipset_command, tvb, 4, 2, data->encoding);
break;
}
@ -1154,19 +1158,22 @@ dissect_netlink_netfilter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, v
info.data = data;
info.hw_protocol = 0;
offset = 0;
switch (data->type >> 8) {
case WS_NFNL_SUBSYS_QUEUE:
offset = dissect_netfilter_queue(tvb, &info, tree, offset);
offset = dissect_netfilter_queue(tvb, &info, nlmsg_tree, offset);
break;
case WS_NFNL_SUBSYS_ULOG:
offset = dissect_netfilter_ulog(tvb, &info, tree, offset);
offset = dissect_netfilter_ulog(tvb, &info, nlmsg_tree, offset);
break;
case WS_NFNL_SUBSYS_IPSET:
offset = dissect_netfilter_ipset(tvb, &info, tree, offset);
offset = dissect_netfilter_ipset(tvb, &info, nlmsg_tree, offset);
break;
default:
call_data_dissector(tvb_new_subset_remaining(tvb, offset), pinfo, nlmsg_tree);
offset = tvb_reported_length(tvb);
break;
}
@ -1242,8 +1249,6 @@ proto_register_netlink_netfilter(void)
&ett_ipset_ip_attr,
};
int proto_netlink_netfilter;
proto_netlink_netfilter = proto_register_protocol("Linux netlink netfilter protocol", "netfilter", "netlink-netfilter" );
hfi_netlink_netfilter = proto_registrar_get_nth(proto_netlink_netfilter);

View File

@ -287,6 +287,8 @@ enum {
WS_NUD_PERMANENT = 0x80
};
static int proto_netlink_route;
static dissector_handle_t netlink_route_handle;
static header_field_info *hfi_netlink_route = NULL;
@ -970,32 +972,24 @@ static header_field_info hfi_netlink_route_nltype NETLINK_ROUTE_HFI_INIT =
static int
dissect_netlink_route(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *_data)
{
int offset;
struct netlink_route_info info;
struct packet_netlink_data *data = NULL;
struct packet_netlink_data *data = (struct packet_netlink_data *)_data;
proto_tree *nlmsg_tree;
proto_item *pi;
int offset = 0;
if (_data) {
if (((struct packet_netlink_data *) _data)->magic == PACKET_NETLINK_MAGIC)
data = (struct packet_netlink_data *) _data;
}
DISSECTOR_ASSERT(data);
DISSECTOR_ASSERT(data && data->magic == PACKET_NETLINK_MAGIC);
col_set_str(pinfo->cinfo, COL_PROTOCOL, "Netlink route");
col_clear(pinfo->cinfo, COL_INFO);
offset = 0;
pi = proto_tree_add_item(tree, proto_registrar_get_nth(proto_netlink_route), tvb, 0, -1, ENC_NA);
nlmsg_tree = proto_item_add_subtree(pi, ett_netlink_route);
if (tree) {
proto_item_set_text(tree, "Linux rtnetlink (route netlink) message");
/* XXX, from header tvb */
proto_tree_add_uint(tree, &hfi_netlink_route_nltype, NULL, 0, 0, data->type);
}
/* Netlink message header (nlmsghdr) */
offset = dissect_netlink_header(tvb, nlmsg_tree, offset, data->encoding, &hfi_netlink_route_nltype, NULL);
info.encoding = data->encoding;
info.pinfo = pinfo;
info.data = data;
@ -1005,9 +999,9 @@ dissect_netlink_route(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
case WS_RTM_GETLINK:
/* backward compatibility with legacy tools; 16 is sizeof(struct ifinfomsg) */
info.legacy = (data->type == WS_RTM_GETLINK) && (tvb_reported_length_remaining(tvb, offset) < 16);
offset = dissect_netlink_route_ifinfomsg(tvb, &info, tree, offset);
offset = dissect_netlink_route_ifinfomsg(tvb, &info, nlmsg_tree, offset);
/* Optional attributes */
offset = dissect_netlink_route_attributes(tvb, &hfi_netlink_route_ifla_attr_type, &info, tree, offset, dissect_netlink_route_ifla_attrs);
offset = dissect_netlink_route_attributes(tvb, &hfi_netlink_route_ifla_attr_type, &info, nlmsg_tree, offset, dissect_netlink_route_ifla_attrs);
break;
case WS_RTM_NEWADDR:
@ -1015,9 +1009,9 @@ dissect_netlink_route(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
case WS_RTM_GETADDR:
/* backward compatibility with legacy tools; 8 is sizeof(struct ifaddrmsg) */
info.legacy = (data->type == WS_RTM_GETADDR) && (tvb_reported_length_remaining(tvb, offset) < 8);
offset = dissect_netlink_route_ifaddrmsg(tvb, &info, tree, offset);
offset = dissect_netlink_route_ifaddrmsg(tvb, &info, nlmsg_tree, offset);
/* Optional attributes */
offset = dissect_netlink_route_attributes(tvb, &hfi_netlink_route_ifa_attr_type, &info, tree, offset, dissect_netlink_route_ifa_attrs);
offset = dissect_netlink_route_attributes(tvb, &hfi_netlink_route_ifa_attr_type, &info, nlmsg_tree, offset, dissect_netlink_route_ifa_attrs);
break;
case WS_RTM_NEWROUTE:
@ -1025,9 +1019,9 @@ dissect_netlink_route(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
case WS_RTM_GETROUTE:
/* backward compatibility with legacy tools; 12 is sizeof(struct rtmsg) */
info.legacy = (data->type == WS_RTM_GETROUTE) && (tvb_reported_length_remaining(tvb, offset) < 12);
offset = dissect_netlink_route_rtmsg(tvb, &info, tree, offset);
offset = dissect_netlink_route_rtmsg(tvb, &info, nlmsg_tree, offset);
/* Optional attributes */
offset = dissect_netlink_route_attributes(tvb, &hfi_netlink_route_rta_attr_type, &info, tree, offset, dissect_netlink_route_route_attrs);
offset = dissect_netlink_route_attributes(tvb, &hfi_netlink_route_rta_attr_type, &info, nlmsg_tree, offset, dissect_netlink_route_route_attrs);
break;
case WS_RTM_NEWNEIGH:
@ -1035,7 +1029,7 @@ dissect_netlink_route(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
case WS_RTM_GETNEIGH:
/* backward compatibility with legacy tools; 12 is sizeof(struct ndmsg) */
info.legacy = (data->type == WS_RTM_GETNEIGH) && (tvb_reported_length_remaining(tvb, offset) < 12);
offset = dissect_netlink_route_ndmsg(tvb, &info, tree, offset);
offset = dissect_netlink_route_ndmsg(tvb, &info, nlmsg_tree, offset);
break;
}
@ -1103,8 +1097,6 @@ proto_register_netlink_route(void)
&ett_netlink_route_if_flags
};
int proto_netlink_route;
proto_netlink_route = proto_register_protocol("Linux rtnetlink (route netlink) protocol", "rtnetlink", "netlink-route" );
hfi_netlink_route = proto_registrar_get_nth(proto_netlink_route);

View File

@ -39,6 +39,8 @@ typedef struct {
int encoding; /* copy of data->encoding */
} netlink_sock_diag_info_t;
static int proto_netlink_sock_diag;
static dissector_handle_t netlink_sock_diag_handle;
static header_field_info *hfi_netlink_sock_diag = NULL;
@ -1103,26 +1105,22 @@ static header_field_info hfi_netlink_sock_diag_nltype NETLINK_SOCK_DIAG_HFI_INIT
static int
dissect_netlink_sock_diag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *_data)
{
struct packet_netlink_data *data = NULL;
struct packet_netlink_data *data = (struct packet_netlink_data *)_data;
netlink_sock_diag_info_t info;
int offset;
proto_tree *nlmsg_tree;
proto_item *pi;
int offset = 0;
if (_data) {
if (((struct packet_netlink_data *) _data)->magic == PACKET_NETLINK_MAGIC)
data = (struct packet_netlink_data *) _data;
}
DISSECTOR_ASSERT(data);
DISSECTOR_ASSERT(data && data->magic == PACKET_NETLINK_MAGIC);
col_set_str(pinfo->cinfo, COL_PROTOCOL, "Netlink sock diag");
col_clear(pinfo->cinfo, COL_INFO);
if (tree) {
proto_item_set_text(tree, "Linux netlink sock diag message");
pi = proto_tree_add_item(tree, proto_registrar_get_nth(proto_netlink_sock_diag), tvb, 0, -1, ENC_NA);
nlmsg_tree = proto_item_add_subtree(pi, ett_netlink_sock_diag);
/* XXX, from header tvb */
proto_tree_add_uint(tree, &hfi_netlink_sock_diag_nltype, NULL, 0, 0, data->type);
}
/* Netlink message header (nlmsghdr) */
offset = dissect_netlink_header(tvb, nlmsg_tree, offset, data->encoding, &hfi_netlink_sock_diag_nltype, NULL);
info.encoding = data->encoding;
info.pinfo = pinfo;
@ -1137,7 +1135,7 @@ dissect_netlink_sock_diag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, v
break;
case WS_SOCK_DIAG_BY_FAMILY:
offset = dissect_sock_diag_by_family(tvb, &info, tree, offset);
offset = dissect_sock_diag_by_family(tvb, &info, nlmsg_tree, offset);
break;
}
@ -1221,8 +1219,6 @@ proto_register_netlink_sock_diag(void)
&ett_netlink_sock_diag_attr
};
int proto_netlink_sock_diag;
proto_netlink_sock_diag = proto_register_protocol("Linux netlink sock diag protocol", "sock_diag", "netlink-sock_diag" );
hfi_netlink_sock_diag = proto_registrar_get_nth(proto_netlink_sock_diag);

View File

@ -340,27 +340,43 @@ dissect_netlink_attributes_array(tvbuff_t *tvb, header_field_info *hfi_type, int
return dissect_netlink_attributes_common(tvb, hfi_type, ett_array, ett_attrib, data, nl_data, tree, offset, length, cb);
}
static int
dissect_netlink_hdr(tvbuff_t *tvb, proto_tree *tree, int offset, int encoding, guint16 *type, guint32 *port_id)
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");
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_guint16(tvb, offset, encoding);
if (hdr_type >= WS_NLMSG_MIN_TYPE) {
proto_item_set_text(pi_type, "Message type: Protocol-specific (0x%04x)", hdr_type);
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 (type) {
*type = hdr_type;
if (pi_type) {
*pi_type = pi;
}
/* 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;
@ -382,7 +398,7 @@ dissect_netlink_hdr(tvbuff_t *tvb, proto_tree *tree, int offset, int encoding, g
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;
@ -403,7 +419,7 @@ dissect_netlink_error(tvbuff_t *tvb, proto_tree *tree, int offset, int encoding)
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
@ -462,21 +478,17 @@ dissect_netlink(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *_data
}
while (tvb_reported_length_remaining(tvb, offset) >= 16) {
struct packet_netlink_data data;
int pkt_end_offset;
guint16 msg_type;
guint32 pkt_len;
guint32 port_id;
proto_tree *fh_msg;
gboolean dissected = FALSE;
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,
@ -492,7 +504,9 @@ dissect_netlink(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *_data
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)
@ -500,17 +514,37 @@ dissect_netlink(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *_data
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))
if (dissector_try_uint_new(netlink_dissector_table, protocol, next_tvb, pinfo, tree, TRUE, &data)) {
dissected = TRUE;
}
}
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;

View File

@ -105,6 +105,12 @@ struct packet_netlink_data {
guint16 type;
};
/**
* Dissects the Netlink message header (struct nlmsghdr). The "hfi_type" field
* is added for the "nlmsg_type" field and returned into pi_type.
*/
int dissect_netlink_header(tvbuff_t *tvb, proto_tree *tree, int offset, int encoding, header_field_info *hfi_type, proto_item **pi_type);
typedef int netlink_attributes_cb_t(tvbuff_t *, void *data, proto_tree *, int nla_type, int offset, int len);
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);