Repository navigation
libpcap: get MAC addresses on Darwin, *BSD and illumos correctly - #5224
Conversation
It fixes a bug where scapy ended up with "00:00:00:00:00:00" on Darwin and *BSD and with MAC addresses like "04:00:06:03:06:00" (where 4, 6, 3 and 6 were the ifindex, the type, the name length and the address length accordingly) on illumos. With this patch applied scapy extracts the lengths from the sockaddr_dl structure, skips the names and gets the MAC addresses by analogy with what it already did before 07dedfd was merged. The bug didn't affect Linux because AF_LINK isn't there so ioctl with SIOCGIFHWADDR is used instead. It was tested on Darwin, FreeBSD, NetBSD, OpenBSD, illumos and Linux. AI-Assisted: no
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #5224 +/- ##
==========================================
+ Coverage 80.84% 81.14% +0.30%
==========================================
Files 393 393
Lines 98325 98327 +2
==========================================
+ Hits 79488 79791 +303
+ Misses 18837 18536 -301
🚀 New features to boost your workflow:
|
| # https://github.com/illumos/illumos-gate/blob/6a2df4aa5381599179ab6afb3165db81960dee35/usr/src/uts/common/net/if_dl.h#L65-L76 | ||
| # https://github.com/NetBSD/src/blob/bc2d21a8880597d1affe4ec581e0b65637258dc4/sys/net/if_dl.h#L78-L91 | ||
| # https://github.com/openbsd/src/blob/ce063bbc9d6190c8ba255f11feb6910dc3541f29/sys/net/if_dl.h#L56-L70 | ||
| sockaddr_dl = ccast(ap, POINTER(c_ubyte)) |
There was a problem hiding this comment.
Thanks a lot for the PR. Wasn't it possible to keep the structure? This is a bit harder to read. I'm not sure I understand what happened wrong in the previous structure, since 1 and 2 bytes-long fields should be pretty much padded the same on all plateforms. Thanks !
There was a problem hiding this comment.
The sockaddr_dl structure is slightly different on different platforms but the main difference is that the sdl_data array (where MAC addresses are kept) comes with different sizes and on all those platforms apart from illumos sdl_data is a minimum work area that can be larger so it's still necessary to cast it to the plain pointer and add the length anyway. I removed the structure when all the platform-specific if clauses started to grow and obscure what actually happens there. When I got to NetBSD (where dl_data is kept in a nested dl_addr structure) I decided to stop trying to describe what the sockaddr_dl structure looks like and switched to the pointer.
There was a problem hiding this comment.
If the header (not the data part) is different, I'm not sure I understand how your code works.
If the header is the same (except sdl_data), do you think we could make a
sockaddr_dl_hdr structure that doesn't include it? I'm not a fan of the "take offset 5, then offset 6", so if you could at least read the lengths using a structure I'd prefer that.
Sorry if it's annoying
There was a problem hiding this comment.
If the header (not the data part) is different, I'm not sure I understand how your code works
It works because the parts scapy uses to get MAC addresses are the same. For example on illumos the first member of the structure is ushort_t sdl_family (2 bytes) while on *BSD those two bytes contain the first two members
uint8_t sdl_len; /* Total length of sockaddr */
sa_family_t sdl_family; /* AF_LINK */but it doesn't matter because those members aren't used. The nested dl_addr structure on NetBSD is
struct dl_addr {
uint8_t dl_type; /* interface type */
uint8_t dl_nlen; /* interface name length, no trailing 0 reqd. */
uint8_t dl_alen; /* link level address length */
uint8_t dl_slen; /* link layer selector length */
char dl_data[24]; /*
* minimum work area, can be larger; contains
* both if name and ll address; big enough for
* IFNAMSIZ plus 8byte ll addr.
*/
};and it's kept within sockaddr_dl like
struct sockaddr_dl {
uint8_t sdl_len; /* Total length of sockaddr */
sa_family_t sdl_family; /* AF_LINK */
uint16_t sdl_index; /* if != 0, system given index for interface */
struct dl_addr sdl_addr;
...
so the name length and the address length are effectively kept in the 5th and 6th bytes anyway and there is no need to access them like sdl_addr.dl_nlen.
I'm not a fan of the "take offset 5, then offset 6", so if you could at least read the lengths using a structure I'd prefer that
I'm not a fan of those things either but the all the platform-specific if clauses I used got really ugly.
There was a problem hiding this comment.
Either way I'll convert it to draft to see whether I can come up with something less ugly than raw offsets or a bunch of ifs describing those structures and a bunch of ifs using those structures.
There was a problem hiding this comment.
Nah, it's alright. Your reasoning is fine and is probably the easiest option
|
Thanks a lot for the PR, again, and for taking the time to test this. |
It fixes a bug where scapy ended up with "00:00:00:00:00:00" on Darwin and *BSD and with MAC addresses like "04:00:06:03:06:00" (where 4, 6, 3 and 6 were the ifindex, the type, the name length and the address length accordingly) on illumos. With this patch applied scapy extracts the lengths from the sockaddr_dl structure, skips the names and gets the MAC addresses by analogy with what it already did before 07dedfd was merged. The bug didn't affect Linux because AF_LINK isn't there so ioctl with SIOCGIFHWADDR is used instead.
It was tested on Darwin, FreeBSD, NetBSD, OpenBSD, illumos and Linux.