summaryrefslogtreecommitdiffstats
path: root/print-usb.c
blob: 98bd91b20fb3f633d6f5e437067683c6c8a16738 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/*
 * Copyright 2009 Bert Vermeulen <bert@biot.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: (1) source code distributions
 * retain the above copyright notice and this paragraph in its entirety, (2)
 * distributions including binary code include the above copyright notice and
 * this paragraph in its entirety in the documentation or other materials
 * provided with the distribution, and (3) all advertising materials mentioning
 * features or use of this software display the following acknowledgement:
 * ``This product includes software developed by Paolo Abeni.''
 * The name of author may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Support for USB packets
 *
 */

/* \summary: USB printer */

#include <config.h>

#include "netdissect-stdinc.h"

#define ND_LONGJMP_FROM_TCHECK
#include "netdissect.h"
#include "extract.h"

#ifdef DLT_USB_LINUX
/*
 * possible transfer mode
 */
#define URB_TRANSFER_IN   0x80
#define URB_ISOCHRONOUS   0x0
#define URB_INTERRUPT     0x1
#define URB_CONTROL       0x2
#define URB_BULK          0x3

/*
 * possible event type
 */
#define URB_SUBMIT        'S'
#define URB_COMPLETE      'C'
#define URB_ERROR         'E'

/*
 * USB setup header as defined in USB specification.
 * Appears at the front of each Control S-type packet in DLT_USB captures.
 */
typedef struct _usb_setup {
	nd_uint8_t bmRequestType;
	nd_uint8_t bRequest;
	nd_uint16_t wValue;
	nd_uint16_t wIndex;
	nd_uint16_t wLength;
} pcap_usb_setup;

/*
 * Information from the URB for Isochronous transfers.
 */
typedef struct _iso_rec {
	nd_int32_t	error_count;
	nd_int32_t	numdesc;
} iso_rec;

/*
 * Header prepended by linux kernel to each event.
 * Appears at the front of each packet in DLT_USB_LINUX captures.
 */
typedef struct _usb_header {
	nd_uint64_t id;
	nd_uint8_t event_type;
	nd_uint8_t transfer_type;
	nd_uint8_t endpoint_number;
	nd_uint8_t device_address;
	nd_uint16_t bus_id;
	nd_uint8_t setup_flag;/*if !=0 the urb setup header is not present*/
	nd_uint8_t data_flag; /*if !=0 no urb data is present*/
	nd_int64_t ts_sec;
	nd_int32_t ts_usec;
	nd_int32_t status;
	nd_uint32_t urb_len;
	nd_uint32_t data_len; /* amount of urb data really present in this event*/
	pcap_usb_setup setup;
} pcap_usb_header;

/*
 * Header prepended by linux kernel to each event for the 2.6.31
 * and later kernels; for the 2.6.21 through 2.6.30 kernels, the
 * "iso_rec" information, and the fields starting with "interval"
 * are zeroed-out padding fields.
 *
 * Appears at the front of each packet in DLT_USB_LINUX_MMAPPED captures.
 */
typedef struct _usb_header_mmapped {
	nd_uint64_t id;
	nd_uint8_t event_type;
	nd_uint8_t transfer_type;
	nd_uint8_t endpoint_number;
	nd_uint8_t device_address;
	nd_uint16_t bus_id;
	nd_uint8_t setup_flag;/*if !=0 the urb setup header is not present*/
	nd_uint8_t data_flag; /*if !=0 no urb data is present*/
	nd_int64_t ts_sec;
	nd_int32_t ts_usec;
	nd_int32_t status;
	nd_uint32_t urb_len;
	nd_uint32_t data_len; /* amount of urb data really present in this event*/
	union {
		pcap_usb_setup setup;
		iso_rec iso;
	} s;
	nd_int32_t interval;	/* for Interrupt and Isochronous events */
	nd_int32_t start_frame;	/* for Isochronous events */
	nd_uint32_t xfer_flags;	/* copy of URB's transfer flags */
	nd_uint32_t ndesc;	/* number of isochronous descriptors */
} pcap_usb_header_mmapped;

/*
 * Isochronous descriptors; for isochronous transfers there might be
 * one or more of these at the beginning of the packet data.  The
 * number of descriptors is given by the "ndesc" field in the header;
 * as indicated, in older kernels that don't put the descriptors at
 * the beginning of the packet, that field is zeroed out, so that field
 * can be trusted even in captures from older kernels.
 */
typedef struct _usb_isodesc {
	nd_int32_t	status;
	nd_uint32_t	offset;
	nd_uint32_t	len;
	nd_byte		pad[4];
} usb_isodesc;


/* returns direction: 1=inbound 2=outbound -1=invalid */
static int
get_direction(int transfer_type, int event_type)
{
	int direction;

	direction = -1;
	switch(transfer_type){
	case URB_BULK:
	case URB_CONTROL:
	case URB_ISOCHRONOUS:
		switch(event_type) {
		case URB_SUBMIT:
			direction = 2;
			break;
		case URB_COMPLETE:
		case URB_ERROR:
			direction = 1;
			break;
		default:
			direction = -1;
		}
		break;
	case URB_INTERRUPT:
		switch(event_type) {
		case URB_SUBMIT:
			direction = 1;
			break;
		case URB_COMPLETE:
		case URB_ERROR:
			direction = 2;
			break;
		default:
			direction = -1;
		}
		break;
	 default:
		direction = -1;
	}

	return direction;
}

static void
usb_header_print(netdissect_options *ndo, const pcap_usb_header *uh)
{
	int direction;
	uint8_t transfer_type, event_type;

	ndo->ndo_protocol = "usb";

	nd_print_protocol_caps(ndo);
	if (ndo->ndo_qflag)
		return;

	ND_PRINT(" ");
	transfer_type = GET_U_1(uh->transfer_type);
	switch(transfer_type) {
		case URB_ISOCHRONOUS:
			ND_PRINT("ISOCHRONOUS");
			break;
		case URB_INTERRUPT:
			ND_PRINT("INTERRUPT");
			break;
		case URB_CONTROL:
			ND_PRINT("CONTROL");
			break;
		case URB_BULK:
			ND_PRINT("BULK");
			break;
		default:
			ND_PRINT(" ?");
	}

	event_type = GET_U_1(uh->event_type);
	switch(event_type) {
		case URB_SUBMIT:
			ND_PRINT(" SUBMIT");
			break;
		case URB_COMPLETE:
			ND_PRINT(" COMPLETE");
			break;
		case URB_ERROR:
			ND_PRINT(" ERROR");
			break;
		default:
			ND_PRINT(" ?");
	}

	direction = get_direction(transfer_type, event_type);
	if(direction == 1)
		ND_PRINT(" from");
	else if(direction == 2)
		ND_PRINT(" to");
	ND_PRINT(" %u:%u:%u", GET_HE_U_2(uh->bus_id),
		 GET_U_1(uh->device_address),
		 GET_U_1(uh->endpoint_number) & 0x7f);
}

/*
 * This is the top level routine of the printer for captures with a
 * 48-byte header.
 *
 * 'p' points to the header of the packet, 'h->ts' is the timestamp,
 * 'h->len' is the length of the packet off the wire, and 'h->caplen'
 * is the number of bytes actually captured.
 */
void
usb_linux_48_byte_if_print(netdissect_options *ndo,
                           const struct pcap_pkthdr *h _U_, const u_char *p)
{
	ndo->ndo_protocol = "usb_linux_48_byte";
	ND_TCHECK_LEN(p, sizeof(pcap_usb_header));
	ndo->ndo_ll_hdr_len += sizeof (pcap_usb_header);

	usb_header_print(ndo, (const pcap_usb_header *) p);
}

#ifdef DLT_USB_LINUX_MMAPPED
/*
 * This is the top level routine of the printer for captures with a
 * 64-byte header.
 *
 * 'p' points to the header of the packet, 'h->ts' is the timestamp,
 * 'h->len' is the length of the packet off the wire, and 'h->caplen'
 * is the number of bytes actually captured.
 */
void
usb_linux_64_byte_if_print(netdissect_options *ndo,
                           const struct pcap_pkthdr *h _U_, const u_char *p)
{
	ndo->ndo_protocol = "usb_linux_64_byte";
	ND_TCHECK_LEN(p, sizeof(pcap_usb_header_mmapped));
	ndo->ndo_ll_hdr_len += sizeof (pcap_usb_header_mmapped);

	usb_header_print(ndo, (const pcap_usb_header *) p);
}
#endif /* DLT_USB_LINUX_MMAPPED */

#endif /* DLT_USB_LINUX */