3 * Near Field Communication nfctool
5 * Copyright (C) 2012 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include <near/nfc_copy.h>
32 #include "snep-decode.h"
33 #include "llcp-decode.h"
35 /* Raw socket + LLCP headers */
36 #define RAW_LLCP_HEADERS_SIZE 4
38 #define LLCP_PTYPE_SYMM 0
39 #define LLCP_PTYPE_PAX 1
40 #define LLCP_PTYPE_AGF 2
41 #define LLCP_PTYPE_UI 3
42 #define LLCP_PTYPE_CONNECT 4
43 #define LLCP_PTYPE_DISC 5
44 #define LLCP_PTYPE_CC 6
45 #define LLCP_PTYPE_DM 7
46 #define LLCP_PTYPE_FRMR 8
47 #define LLCP_PTYPE_SNL 9
48 #define LLCP_PTYPE_I 12
49 #define LLCP_PTYPE_RR 13
50 #define LLCP_PTYPE_RNR 14
52 #define LLCP_DM_NORMAL 0x00
53 #define LLCP_DM_NO_ACTIVE_CONN 0x01
54 #define LLCP_DM_NOT_BOUND 0x02
55 #define LLCP_DM_REJECTED 0x03
56 #define LLCP_DM_PERM_SAP_FAILURE 0x10
57 #define LLCP_DM_PERM_ALL_SAP_FAILURE 0x11
58 #define LLCP_DM_TMP_SAP_FAILURE 0x20
59 #define LLCP_DM_TMP_ALL_SAP_FAILURE 0x21
62 LLCP_PARAM_VERSION = 1,
72 LLCP_PARAM_MIN = LLCP_PARAM_VERSION,
73 LLCP_PARAM_MAX = LLCP_PARAM_SDRES
76 static guint8 llcp_param_length[] = {
89 static char *llcp_ptype_str[] = {
91 "Parameter Exchange (PAX)",
92 "Aggregated Frame (AGF)",
93 "Unnumbered Information (UI)",
96 "Connection Complete (CC)",
97 "Disconnected Mode (DM)",
98 "Frame Reject (FRMR)",
99 "Service Name Lookup (SNL)",
103 "Receive Ready (RR)",
104 "Receive Not Ready (RNR)",
109 static char *llcp_ptype_short_str[] = {
129 static const gchar *llcp_param_str[] = {
132 "Maximum Information Unit Extensions",
133 "Well-Known Service List",
135 "Receive Window Size",
138 "Service Discovery Request",
139 "Service Discovery Response"
142 #define llcp_printf_header(prefix, color, fmt, ...) \
143 print_indent_prefix( LLCP_HEADER_INDENT,\
145 LLCP_COLOR, fmt, ## __VA_ARGS__)
147 #define llcp_printf_msg(fmt, ...) print_indent(LLCP_MSG_INDENT, \
148 LLCP_COLOR, fmt, ## __VA_ARGS__)
150 #define llcp_printf_error(fmt, ...) print_indent(LLCP_MSG_INDENT, \
151 COLOR_ERROR, fmt, ## __VA_ARGS__)
153 #define llcp_get_param_str(param_type) llcp_param_str[param_type]
155 #define llcp_get_param_len(param_type) llcp_param_length[param_type]
157 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
159 static struct timeval start_timestamp;
161 static void llcp_print_params(struct sniffer_packet *packet)
164 guint16 miux, wks, tid;
169 gchar *sn, param_str[64];
171 while (packet->llcp.data_len - offset >= 3) {
172 param = packet->llcp.data + offset;
173 rmng = packet->llcp.data_len - offset;
175 if (param[0] < LLCP_PARAM_MIN || param[0] > LLCP_PARAM_MAX) {
176 llcp_printf_error("Error decoding params");
180 param_len = llcp_get_param_len(param[0]);
183 param_len = param[1];
185 if (param_len != param[1] || rmng < 2u + param_len) {
186 llcp_printf_error("Error decoding params");
190 switch ((enum llcp_param_t)param[0]) {
191 case LLCP_PARAM_VERSION:
192 major = (param[2] & 0xF0) >> 4;
193 minor = param[2] & 0x0F;
194 sprintf(param_str, "%d.%d", major, minor);
197 case LLCP_PARAM_MIUX:
198 miux = ((param[2] & 0x07) << 8) | param[3];
199 sprintf(param_str, "%d", miux);
203 wks = (param[2] << 8) | param[3];
204 sprintf(param_str, "0x%02hX", wks);
208 sprintf(param_str, "%d", param[2]);
212 sprintf(param_str, "%d", param[2] & 0x0F);
216 sn = g_strndup((gchar *)param + 2, param_len);
217 sprintf(param_str, "%s", sn);
222 sprintf(param_str, "0x%X", param[2] & 0x03);
225 case LLCP_PARAM_SDREQ:
227 sn = g_strndup((gchar *)param + 3, param_len - 1);
228 sprintf(param_str, "TID:%d, SN:%s", tid, sn);
232 case LLCP_PARAM_SDRES:
233 sprintf(param_str, "TID:%d, SAP:%d", param[2], param[3] & 0x3F);
237 llcp_printf_msg("%s: %s", llcp_get_param_str(param[0]),
240 offset += 2 + param_len;
244 static int llcp_decode_packet(guint8 *data, guint32 data_len,
245 struct sniffer_packet *packet)
247 if (data_len < RAW_LLCP_HEADERS_SIZE)
250 memset(packet, 0, sizeof(struct sniffer_packet));
252 /* LLCP raw socket header */
253 packet->adapter_idx = data[0];
254 packet->direction = data[1] & 0x01;
257 if (packet->direction == NFC_LLCP_DIRECTION_TX) {
258 packet->llcp.remote_sap = (data[2] & 0xFC) >> 2;
259 packet->llcp.local_sap = data[3] & 0x3F;
261 packet->llcp.remote_sap = data[3] & 0x3F;
262 packet->llcp.local_sap = (data[2] & 0xFC) >> 2;
265 packet->llcp.ptype = ((data[2] & 0x03) << 2) | ((data[3] & 0xC0) >> 6);
267 if (packet->llcp.ptype >= ARRAY_SIZE(llcp_ptype_str))
270 packet->llcp.data = data + RAW_LLCP_HEADERS_SIZE;
271 packet->llcp.data_len = data_len - RAW_LLCP_HEADERS_SIZE;
274 if (packet->llcp.ptype >= LLCP_PTYPE_I) {
275 if (packet->llcp.data_len == 0)
278 packet->llcp.send_seq = ((packet->llcp.data[0] & 0xF0) >> 4);
279 packet->llcp.recv_seq = packet->llcp.data[0] & 0x0F;
282 packet->llcp.data_len--;
288 static void llcp_print_sequence(struct sniffer_packet *packet)
290 llcp_printf_msg("N(S):%d N(R):%d",
291 packet->llcp.send_seq, packet->llcp.recv_seq);
294 static int llcp_print_agf(struct sniffer_packet *packet,
295 struct timeval *timestamp)
304 if (packet->llcp.data_len < 2) {
305 llcp_printf_error("Error parsing AGF PDU");
316 while (offset < packet->llcp.data_len - 2) {
317 size = (packet->llcp.data[offset] << 8) |
318 packet->llcp.data[offset + 1];
322 if (size == 0 || offset + size > packet->llcp.data_len) {
323 llcp_printf_error("Error parsing AGF PDU");
328 if (size + NFC_LLCP_RAW_HEADER_SIZE > pdu_size) {
329 pdu_size = size + NFC_LLCP_RAW_HEADER_SIZE;
330 pdu = g_realloc(pdu, pdu_size);
332 pdu[0] = packet->adapter_idx;
333 pdu[1] = packet->direction;
336 memcpy(pdu + NFC_LLCP_RAW_HEADER_SIZE,
337 packet->llcp.data + offset, size);
339 llcp_printf_msg("-- AGF LLC PDU %02u:", count++);
341 llcp_print_pdu(pdu, size + NFC_LLCP_RAW_HEADER_SIZE, timestamp);
346 llcp_printf_msg("-- End of AGF LLC PDUs");
355 static int llcp_print_dm(struct sniffer_packet *packet)
359 if (packet->llcp.data_len != 1)
362 switch (packet->llcp.data[0]) {
365 reason = "Normal disconnect";
368 case LLCP_DM_NO_ACTIVE_CONN:
370 "No active connection for connection-oriented PDU at SAP";
373 case LLCP_DM_NOT_BOUND:
374 reason = "No service bound to target SAP";
377 case LLCP_DM_REJECTED:
378 reason = "CONNECT PDU rejected by service layer";
381 case LLCP_DM_PERM_SAP_FAILURE:
382 reason = "Permanent failure for target SAP";
385 case LLCP_DM_PERM_ALL_SAP_FAILURE:
386 reason = "Permanent failure for any target SAP";
389 case LLCP_DM_TMP_SAP_FAILURE:
390 reason = "Temporary failure for target SAP";
393 case LLCP_DM_TMP_ALL_SAP_FAILURE:
394 reason = "Temporary failure for any target SAP";
398 llcp_printf_msg("Reason: %d (%s)", packet->llcp.data[0], reason);
403 static int llcp_print_i(struct sniffer_packet *packet)
405 llcp_print_sequence(packet);
407 if (packet->llcp.local_sap == opts.snep_sap ||
408 packet->llcp.remote_sap == opts.snep_sap) {
411 err = snep_print_pdu(packet);
413 llcp_printf_error("Error decoding SNEP frame");
418 sniffer_print_hexdump(stdout, packet->llcp.data, packet->llcp.data_len,
419 LLCP_MSG_INDENT, TRUE);
424 static int llcp_print_frmr(struct sniffer_packet *packet)
428 if (packet->llcp.data_len != 4)
431 info = packet->llcp.data[0];
433 if (ptype >= ARRAY_SIZE(llcp_ptype_str))
434 ptype = ARRAY_SIZE(llcp_ptype_str) - 1;
436 llcp_printf_msg("W:%d I:%d R:%d S:%d PTYPE:%s SEQ: %d V(S):"
437 " %d V(R): %d V(SA): %d V(RA): %d",
438 (info & 0x80) >> 7, (info & 0x40) >> 6,
439 (info & 0x20) >> 5, (info & 0x10) >> 4,
440 llcp_ptype_short_str[ptype], packet->llcp.data[1],
441 (packet->llcp.data[2] & 0xF0) >> 4,
442 packet->llcp.data[2] & 0x0F,
443 (packet->llcp.data[3] & 0xF0) >> 4,
444 packet->llcp.data[3] & 0x0F);
449 int llcp_print_pdu(guint8 *data, guint32 data_len, struct timeval *timestamp)
451 struct timeval msg_timestamp;
452 struct sniffer_packet packet;
453 gchar *direction_str, time_str[32];
454 gchar *direction_color;
457 if (timestamp == NULL)
460 if (!timerisset(&start_timestamp))
461 start_timestamp = *timestamp;
463 err = llcp_decode_packet(data, data_len, &packet);
467 if (!opts.dump_symm && packet.llcp.ptype == LLCP_PTYPE_SYMM)
470 if (packet.direction == NFC_LLCP_DIRECTION_RX) {
471 direction_str = ">>";
472 direction_color = COLOR_RED;
474 direction_str = "<<";
475 direction_color = COLOR_GREEN;
478 if (opts.show_timestamp != SNIFFER_SHOW_TIMESTAMP_NONE) {
481 if (opts.show_timestamp == SNIFFER_SHOW_TIMESTAMP_ABS) {
482 msg_timestamp = *timestamp;
484 timersub(timestamp, &start_timestamp, &msg_timestamp);
488 sprintf(time_str, "%c%lu.%06lus", prefix, msg_timestamp.tv_sec,
489 msg_timestamp.tv_usec);
492 llcp_printf_header(direction_str, direction_color,
493 " nfc%d: local:0x%02x remote:0x%02x %s",
494 packet.adapter_idx, packet.llcp.local_sap,
495 packet.llcp.remote_sap, time_str);
497 llcp_printf_msg("%s", llcp_ptype_str[packet.llcp.ptype]);
499 switch (packet.llcp.ptype) {
501 llcp_print_agf(&packet, timestamp);
505 llcp_print_i(&packet);
510 llcp_print_sequence(&packet);
514 case LLCP_PTYPE_CONNECT:
517 llcp_print_params(&packet);
521 llcp_print_dm(&packet);
524 case LLCP_PTYPE_FRMR:
525 llcp_print_frmr(&packet);
529 sniffer_print_hexdump(stdout, packet.llcp.data,
530 packet.llcp.data_len,
531 LLCP_MSG_INDENT, TRUE);
543 void llcp_decode_cleanup(void)
545 timerclear(&start_timestamp);
547 snep_decode_cleanup();
550 int llcp_decode_init(void)
554 timerclear(&start_timestamp);
556 err = snep_decode_init();