问题
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 aSOCK_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
orETH_P_IP
as the protocol argument tosocket
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 typesockaddr_ll
, which is associated with a particular MAC address (EUI-48 address), along with a particular network interface (such aseth0
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 useAF_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 theETH_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?
回答1:
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;
}
来源:https://stackoverflow.com/questions/54056426/af-packet-and-ethernet