3

I'm trying to use socat to send a raw packet to an ethernet interface, but I get "No such device or address". How should I be using socat?

Usage:

$ echo hi | sudo socat - SOCKET-SENDTO:17:3:0:x0003x00000002x0000x00x06xabcdef0123450000

Breakdown:

domain   - 17 (PF_PACKET)
type     -  3 (SOCK_RAW)
protocol -  0
address  - (data representation of a sockaddr structure)
  sockaddr_ll
    family   - (without family)
    protocol - x0003 (ETH_P_ALL (unsigned short))
    ifindex  - x00000002 (from /sys/class/net/eth0/ifindex (int))
    hatype   - x0000 (0 on send (unsigned short))
    pkttype  - x00   (0 on send (unsigned char))
    halen    - x06   (ETH_ALEN (unsigned char))
    addr     - xabcdef0123450000 (a MAC address on the same segment as ifindex, (unsigned char[8]))

Result:

$ echo hi | sudo socat - SOCKET-SENDTO:17:3:0:x0003x00000002x0000x00x06xabcdef0123450000
2020/05/29 16:05:29 socat[339113] E sendto(5, 0x555fd8ffac70, 3, 0, AF=17 AF=17 0x00030000000200000006abcdef01, 20): No such device or address
s1037989
  • 161
  • 6

1 Answers1

3

Most specifically, the problem was with my STDIN (hi). I mistakenly believed that STDIN was just the packet payload; however, it's actually expecting the entire packet. Also, I discovered that sockaddr->protocol and sockaddr->ifindex (and perhaps sockaddr->hatype as well, but it's all zeros, and perhaps sockaddr->pkttype and sockaddr->halen as well, but they are both a single byte) are byte order reversed. I couldn't find any documentation to support that, but my experience validates that's what works.

So the socat command I'm trying to create is really just an interface to sendto(). And, as such, the buffer parameter (the entire Ethernet packet) is populated by, in this case, STDIN. The rest of the command line arguments are the rest of the sendto() parameters.

The final working raw socket SOCKET-SENDTO command is this:

$ echo -n -e "\xab\xcd\xef\x01\x23\x45\x01\x23\x45\xab\xcd\xef\x00\x02\x68\x69" | sudo socat - SOCKET-SENDTO:17:3:0:x0300x02000000x0000x00x06xabcdef0123450000

Usage:

echo
  -e (enable interpretation of backslash escapes, each byte needs a \x)
  -n (do not output the trailing newline)
  "\xab\xcd\xef\x01\x23\x45 (dst_mac)
   \x01\x23\x45\xab\xcd\xef (src_mac)
   \x00\x02         (length of payload)
   \x68\x69         (payload ("hi" in this case))
  "
domain   - 17 (PF_PACKET, from `procan -c`)
type     -  3 (SOCK_RAW, from `procan -c`)
protocol -  0
address  - (data representation of a sockaddr structure, only the beginning needs an 'x', the rest are optional)
  sockaddr_ll
    family   - (without family)
    protocol - x0300 (ETH_P_ALL (unsigned short), from if_ether.h)
    ifindex  - x02000000 (interface to use for sending, from /sys/class/net/eth0/ifindex (int))
    hatype   - x0000 (0 on send (unsigned short))
    pkttype  - x00   (0 on send (unsigned char))
    halen    - x06   (ETH_ALEN (unsigned char), from if_ether.h)
    addr     - xabcdef0123450000 (dest MAC address on the same segment as ifindex, (unsigned char[8]))

Edit: Thanks to @meuh for some additional insight:

Ethernet packets are, by definition, big-endian or network order. Therefore, the data types of the sockaddr structure are big-endian, but as @meuh pointed out, my machine (x86) is little-endian. Therefore the bytes, for each member of the struct, need to be reversed. What caught me off guard is that the addr member of type char didn't need to be reversed. It's because addr is an array of char. Each element of the array, like the others struct members, needs to be reversed, but they are only one byte in length; but the data going into the array does not need to be reversed.

s1037989
  • 161
  • 6
  • 2
    Thanks for taking the time to post this answer, as there are no examples of how to use this socat address. Network bit and byte order is big-endian for ethernet, and I think there has to be byte swapping done for some fields in the kernel on little-endian machines, which presumably cannot be done for raw sockets/packets. – meuh Jun 02 '20 at 06:48