views:

174

answers:

2

I'm writing code to send raw Ethernet frames between two Linux boxes. To test this I just want to get a simple client-send and server-receive.

I have the client correctly making packets (I can see them using a packet sniffer).

On the server side I initialize the socket like so:

fd = socket(PF_PACKET, SOCK_RAW, htons(MY_ETH_PROTOCOL));

where MY_ETH_PROTOCOL is a 2 byte constant I use as an ethertype so I don't hear extraneous network traffic.

when I bind this socket to my interface I must pass it a protocol again in the socket_addr struct: socket_address.sll_protocol = htons(MY_ETH_PROTOCOL);
If I compile and run the code like this then it fails. My server does not see the packet. However if I change the code like so:
socket_address.sll_protocol = htons(ETH_P_ALL);
The server then can see the packet sent from the client (as well as many other packets) so I have to do some checking of the packet to see that it matches MY_ETH_PROTOCOL.

But I don't want my server to hear traffic that isn't being sent on the specified protocol so this isn't a solution. How do I do this?

A: 

I've worked around this problem in the past by using a packet filter.

Hand Waving (untested pseudocode)

struct bpf_insn my_filter[] = {
    ...
}

s = socket(PF_PACKET, SOCK_DGRAM, htons(protocol));
struct sock_fprog pf;
pf.filter = my_filter;
pf.len = my_filter_len;
setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf));

sll.sll_family = PF_PACKET;
sll.sll_protocol = htons(protocol);
sll.sll_ifindex = if_nametoindex("eth0");

bind(s, &sll, sizeof(sll));

Error checking and getting the packet filter right is left as an exercise for the reader...

Depending on your application, an alternative that may be easier to get working is libpcap.

bstpierre
+1  A: 

I have resolved the issue.

According to http://linuxreviews.org/dictionary/Ethernet/ referring to the 2 byte field following the MAC addresses:

"values of that field between 64 and 1522 indicated the use of the new 802.3 Ethernet format with a length field, while values of 1536 decimal (0600 hexadecimal) and greater indicated the use of the original DIX or Ethernet II frame format with an EtherType sub-protocol identifier."

so I have to make sure my ethertype is >= 0x0600.

According to http://standards.ieee.org/regauth/ethertype/eth.txt use of 0x88b5 and 0x88b6 is "available for public use for prototype and vendor-specific protocol development." So this is what I am going to use as an ethertype. I shouldn't need any further filtering as the kernel should make sure to only pick up ethernet frames with the right destination MAC address and using that protocol.

dschatz