15

I'm very confused about exactly how the AF_PACKET socket family (for SOCK_RAW sockets) specifically relates to Ethernet (IEEE 802.3).

What I understand so far:

  • I understand the OSI Model, and how Layer 2 technologies like Ethernet fit into the model.

  • I understand that AF_PACKET can be used with a SOCK_RAW socket to receive datagrams that contain a 14-byte Ethernet header, followed by some other higher layer protocol header(s), such as IPv4, IPv6, etc., followed by optionally a transport layer protocol like TCP, and finally a payload.

  • I understand you can pass flags such as ETH_P_ALL or ETH_P_IP as the protocol argument to socket to have the kernel filter packets for you, by only sending you packets containing headers of a certain type.

  • I understand that sockets created with the AF_PACKET family can receive or send to endpoints of type sockaddr_ll, which is associated with a particular MAC address (EUI-48 address), along with a particular network interface (such as eth0 or whatever).

What I DON'T understand:

  • I don't understand if AF_PACKET is supposed to exclusively work with Ethernet devices, as opposed to other Layer 2 technologies, such as Wifi, Bluetooth, Token Ring, Infiniband, etc.

  • I don't understand the relationship between Ethernet devices versus Layer 2 protocols that use the 14-byte Ethernet header. The Ethernet header is 14-bytes, and can be defined as something like: struct eth_hdr { char dest_address[6]; char source_address[6]; uint16_t ethertype; }; In other words, is this header only used with physical Ethernet devices? It seems the answer is no, because if I use AF_PACKET on the loopback interface, I still receive packets containing 14-byte Ethernet headers. But loopback is not an Ethernet device. So why does it receive packets containing Ethernet headers?

  • If AF_PACKET can be used with non-Ethernet devices, does the ETH_P_ALL protocol flag indicate to only accept packets that specifically have a 14-byte Ethernet header?


My question(s):

Does using AF_PACKET imply you are guaranteed to always receive packets with 14-byte Ethernet headers?

If so, does that also imply that AF_PACKET is meant to be used ONLY with Ethernet devices (as opposed to other Layer 2 technologies, like Wifi, Token Ring, Bluetooth, Infiniband, etc.)?

If the answer to either of these questions is NO, then how can an application programatically determine what type of Layer 2 header to expect when receiving a datagram on an AF_PACKET socket?

Siler
  • 8,976
  • 11
  • 64
  • 124
  • From [packet(7)](https://linux.die.net/man/7/packet)? `Packet sockets are used to receive or send raw packets at the device driver (OSI Layer 2) level. They allow the user to implement protocol modules in user space on top of the physical layer.` the protocol used in AF_PACKET must be detected by other means, as the kernel does not support (or is unaware of) the protocol used. AF_PACKET may work with anything, just the kernel doesn't know with what. – KamilCuk Jan 05 '19 at 21:35
  • So, why does the loopback interface receive packets with 14-byte Ethernet headers? – Siler Jan 05 '19 at 21:41
  • An ethernet (IEEE 802.3) frame header is _very_ different than a Wi-Fi (IEEE 802.11) frame header. What you see is a "normalization" of different protocol frame headers to an ethernet frame header. I'm not sure how that plays out with the IEEE protocols that use 64-bit MAC addressing, or protocols that use other addressing or no addressing at all. – Ron Maupin Jan 05 '19 at 21:55
  • 1
    I think the answer would be, that the loopback interface uses a protocol that uses 14-byte ethernet headers (if it does). More specifically, it is [implemented](https://github.com/torvalds/linux/blob/master/drivers/net/loopback.c) with 6 bytes address length and to support TCP/IP protocol. – KamilCuk Jan 05 '19 at 22:21
  • @KamilCuk, neither TCP nor IP care anything about the data-link protocol or its addressing. IP runs perfectly well on ethernet with 48-bit MAC addresses, frame relay with DLCI numbers, ATM with VPI/VCI, or PPP with no addressing. Also, TCP can run on IPv4, IPX, or IPv6, not caring which network protocol carries it. That is the beauty of the layer separation and encapsulation; each layer doesn't care what protocol is used in the other layers. – Ron Maupin Jan 05 '19 at 22:35
  • hmm... oddly, according to wireshark, the wifi card on my laptop seems to use Ethernet II as well. Why is that? – theKidOfArcrania Mar 20 '19 at 05:29

1 Answers1

7

Caveat: This comes from cannibalizing some code I wrote for production software that used PF_PACKET, which was only for ethernet, so it may be incomplete/inaccurate.

You're using ETH_P_ALL which will give you anything. But, there are many ETH_P_* symbols to choose from (e.g. ETH_P_802_3_MIN).

The binding/selection is not based on not merely the socket call, but is based on a given interface as well.

First you need the interface name you want (e.g. eth0) from the list you can get from ifconfig.

Then, get the interface index using ioctl(SIOCGIFINDEX,...) from the interface name [or, you could just hard code it as ifconfig will print them out in index order].

Then, bind to that interface, based on the interface index.

Since you know the type of interface (e.g. you chose eth0 or wifi, etc.), after that, you should be able to digest the physical layer header because you know whether it's struct eth_hdr or not.

Note that there are a number of other SIOCGIF* ioctls that you can use to get a list of interfaces and other information that may allow you to discern the interface type [and, therefore, what physical header to expect].

Anyway, here's some sample code from what I did:

int
init(const char *intf)
// intf -- interface name (e.g. eth0, etc. -- whatever comes from ifconfig)
{
    int err;

#if 1
    int styp = SOCK_RAW;
#else
    int styp = SOCK_DGRAM;
#endif

    int netsock = socket(PF_PACKET,styp,htons(ETH_P_ALL));

    struct ifreq ifr;
    memset(&ifr,0,sizeof(ifr));
    strncpy(ifr.ifr_name,intf,sizeof(ifr.ifr_name));

    // get the index number of the interface
    err = ioctl(netsock,SIOCGIFINDEX,&ifr);
    if (err < 0)
        do_whatever;

    printf("init: IFRIDX ifr_ifindex=%d\n",ifr.ifr_ifindex);
    int ifidx = ifr.ifr_ifindex;

    struct sockaddr_ll addr;
    addr.sll_family = AF_PACKET;
    addr.sll_protocol = htons(ETH_P_ALL);
    addr.sll_ifindex = ifidx;

    err = bind(netsock,(struct sockaddr *) &addr,
        sizeof(struct sockaddr_ll));
    if (err < 0)
        do_whatever;

    return netsock;
}
Craig Estey
  • 30,627
  • 4
  • 24
  • 48