Skip to content

Add support for USB Linux packets (usbmon) #4417

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scapy/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
DLT_BLUETOOTH_HCI_H4_WITH_PHDR = 201
DLT_AX25_KISS = 202
DLT_PPP_WITH_DIR = 204
DLT_USB_LINUX_MMAPPED = 220
DLT_FC_2 = 224
DLT_CAN_SOCKETCAN = 227
if OPENBSD:
Expand Down
101 changes: 93 additions & 8 deletions scapy/layers/usb.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,15 @@
Default USB frames & Basic implementation
"""

# TODO: support USB headers for Linux and Darwin (usbmon/netmon)
# https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-usb.c # noqa: E501

from scapy.config import conf
from scapy.compat import chb
from scapy.data import DLT_USBPCAP
from scapy.data import DLT_USBPCAP, DLT_USB_LINUX, DLT_USB_LINUX_MMAPPED
from scapy.fields import ByteField, XByteField, ByteEnumField, LEShortField, \
LEShortEnumField, LEIntField, LEIntEnumField, XLELongField, \
LenField
LEShortEnumField, LEIntField, LEIntEnumField, LELongField, LESignedIntField, \
XLELongField, LenField, StrFixedLenField, BitField, BitEnumField
from scapy.packet import Packet, bind_top_down


# USBpcap

_usbd_status_codes = {
0x00000000: "Success",
0x40000000: "Pending",
Expand Down Expand Up @@ -77,6 +72,94 @@
}


_requests = {
0x00: "GET_STATUS",
0x01: "CLEAR_FEATURE",
0x03: "SET_FEATURE",
0x05: "SET_ADDRESS",
0x06: "GET_DESCRIPTOR",
0x07: "SET_DESCRIPTOR",
0x08: "GET_CONFIGURATION",
0x09: "SET_CONFIGURATION",
0x0A: "GET_INTERFACE",
0x0B: "SET_INTERFACE",
0x0C: "SYNCH_FRAME",
}

_request_recipients = {
0x00: "Device",
0x01: "Interface",
0x02: "Endpoint",
0x03: "Other",
}

_request_types = {
0x00: "Standard",
0x01: "Class",
0x02: "Vendor",
0x03: "Reserved",
}

_request_directions = {
0: "Device-to-host",
1: "Host-to-device",
}

_directions = {
0: "OUT",
1: "IN",
}

_urb_types = {
ord("S"): "SUBMIT",
ord("C"): "COMPLETE",
ord("E"): "ERROR",
}


class _Setup(Packet):
fields_desc = [BitEnumField("bmRequestType_direction", 0, 1, _request_directions),
BitEnumField("bmRequestType_type", 0, 2, _request_types),
BitEnumField("bmRequestType_recipient", 0, 5, _request_recipients),
ByteEnumField("bRequest", 0, _requests),
LEShortField("wValue", 0),
LEShortField("wIndex", 0),
LEShortField("wLength", 0)]


class USBLinux(Packet):
name = "USBLinux URB"
fields_desc = [XLELongField("urb_id", 0),
ByteEnumField("urb_type", 0, _urb_types),
ByteEnumField("transfer_type", 0, _transfer_types),
BitEnumField("direction", 0, 1, _directions),
BitField("endpoint", 0, 7),
ByteField("device_address", 0),
LEShortField("bus_id", 0),
StrFixedLenField("setup_flag", b"\x00", length=1),
StrFixedLenField("data_flag", b"\x00", length=1),
LELongField("urb_ts_sec", 0),
LESignedIntField("urb_ts_usec", 0),
LESignedIntField("urb_status", 0),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

urb_status is a standard Linux error code. I was hoping I could use the standard Python errno library, but unfortunately that only gives you error codes for the system you're running on. So if you try to use scapy on macOS for example, all the error codes are wrong.

For now I left it as is, we can add in a dictionary or error codes in the future unless there's a better solution.

LEIntField("urb_len", 0),
LEIntField("urb_data_len", 0),
_Setup]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This field is actually a union, for ISO packets it should be:

              struct iso_rec {                /* Only for ISO */
                      int error_count;
                      int numdesc;
              } iso;

But I don't know how to deal with unions here. I tried MultiTypeField and the packets went back to being parsed as Raw.



class _USBLinuxExt(Packet):
name = "USBLinux URB"
fields_desc = [LESignedIntField("interval", 0),
LESignedIntField("start_frame", 0),
LEIntField("xfer_flags", 0),
LEIntField("numdesc", 0)]
Comment on lines +149 to +154
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure about how to handle these extra fields. For example, if I wanted to create a SetFeatureRequest Packet subclass, I would need two versions of it: one for the 48 byte header and one for the 64 byte header. So I broke this out into _USBLinuxExt so we can reuse it for making these different variants of packets.

I was hoping there was a cleaner way to do it, but I couldn't figure anything out. Let me know if there's a better way to do this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could just add those fields at the end of the extended header (instead of putting them in a separate packet), and keep the first field of the extended packet as the non-extended packet. (in which case its fields are copied)



class USBLinuxMmapped(Packet):
name = "USBLinux URB"
fields_desc = [USBLinux,
_USBLinuxExt]


class USBpcap(Packet):
name = "USBpcap URB"
fields_desc = [ByteField("headerLen", None),
Expand Down Expand Up @@ -144,3 +227,5 @@ class USBpcapTransferControl(Packet):
bind_top_down(USBpcap, USBpcapTransferControl, transfer=2)

conf.l2types.register(DLT_USBPCAP, USBpcap)
conf.l2types.register(DLT_USB_LINUX_MMAPPED, USBLinuxMmapped)
conf.l2types.register(DLT_USB_LINUX, USBLinux)
Loading