Skip to content
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

Add support for USB Linux packets (usbmon) #4417

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

gsingh93
Copy link
Contributor

@gsingh93 gsingh93 commented Jun 9, 2024

This PR adds support for packets captured with usbmon on Linux. The usbmon docs are here, although they're inaccurate in a few cases: https://docs.kernel.org/usb/usbmon.html. I mainly relied on the Wireshark implementation here: https://github.com/wireshark/wireshark/blob/master/epan/dissectors/packet-usb.c

DLT_USB_LINUX is the 48 byte header of usbmon, while DLT_USB_LINUX_MMAPPED is the 64 byte header. AFAIK, all captures on modern systems use the 64 byte header, but I'm not sure if this is correct. In any case, I can only really test with the 64 byte header on real PCAPs, and I only added the DLT_USB_LINUX support because it seemed easy enough to do.

@gsingh93 gsingh93 marked this pull request as draft June 9, 2024 01:47
Copy link

codecov bot commented Jun 9, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 81.43%. Comparing base (aff2b98) to head (f8e74c7).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4417      +/-   ##
==========================================
- Coverage   81.56%   81.43%   -0.14%     
==========================================
  Files         352      352              
  Lines       84032    84052      +20     
==========================================
- Hits        68544    68451      -93     
- Misses      15488    15601     +113     
Files Coverage Δ
scapy/data.py 90.27% <100.00%> (+0.02%) ⬆️
scapy/layers/usb.py 95.08% <100.00%> (+2.22%) ⬆️

... and 6 files with indirect coverage changes

Copy link
Contributor Author

@gsingh93 gsingh93 left a comment

Choose a reason for hiding this comment

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

I was thinking of adding request and response packets, but I got stuck. As far as I can tell, there is nothing in a response packet that says what type of response it is. It seems that you need to keep track of the last request to figure out the type of the response you're dissecting. What's a good way to do something like this in scapy?

LESignedIntField("urb_status", 0),
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.

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.

Comment on lines +149 to +154
class _USBLinuxExt(Packet):
name = "USBLinux URB"
fields_desc = [LESignedIntField("interval", 0),
LESignedIntField("start_frame", 0),
LEIntField("xfer_flags", 0),
LEIntField("numdesc", 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.

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)

@gpotter2
Copy link
Member

gpotter2 commented Jun 9, 2024

It seems that you need to keep track of the last request to figure out the type of the response you're dissecting. What's a good way to do something like this in scapy?

You could implement a USBSession or similar, that extends DefaultSession from scapy.sessions. We already have a few sessions class in the code, that might inspire you.

@gsingh93
Copy link
Contributor Author

gsingh93 commented Jul 3, 2024

You could implement a USBSession or similar, that extends DefaultSession from scapy.sessions. We already have a few sessions class in the code, that might inspire you.

Thanks @gpotter2. I looked into this, but it seems like sessions are mainly for sniffing. How can I use a session with rdpcap or a PacketList?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants