An ethernet II header:
- Preamble:42
- Start of Field Delimeter:8
- Destination Host:48
- Source Host:48
- Type:16
- Protocol Payload:N
- CRC:16
- The Preamble starts the Ethernet frame (not available to the OS)
- The SOF Delimiter marks the start of the destination host address field (not available to the OS)
- The Destination Host is the 6 byte MAC address of the target host
- The Source Host is the 6 byte MAC address of the sending host
- The Type represents the protocol held in the payload. Common types are IPv4 (ETH_P_IP (16#0800)), ARP (ETH_P_ARP (16#0806)) and IPv6 (ETH_P_IPV6 (16#86DD)).
- A trailing CRC checksum of the packet (frame check sequence) (usually not available to the OS)
Ethernet frames can apparently include other trailing data, leading to trailing junk when doing packet captures.
When capturing data off the network, it's important to properly calculate the size of the packet. For example, for an IPv4 TCP packet:
IP length - (IP header length * 4) - (TCP offset * 4)
- if the value of the Type field is equal to or less than 1500 bytes (maximum frame length), the frame is Ethernet 802.3 and the value represents the length of the frame
- if the value of the Type field is equal to or greather than 1536, the frame is Ethernet II and the value represents the protocol type of the encapsulated packet
The protocol of 802.3 frames is always IPX.
- if the value of the Type field is between 1500 and 1536, the behaviour is undefined)
<< Dhost:6/bytes, % destination MAC address Shost:6/bytes, % source MAC address Type:16 % protocol type, usually ETH_P_IP >>
Using Erlang to Bridge Packets
To go along with the ARP poisoning, we need a sort of "one armed bridging" to forward packets from our spoofing host to the real destinations. Once we have the raw ethernet frames, doing the bridging is quite simple. For the complete code, see herp on GitHub (yes, the herp is what you get when you've been promiscuous. I hear like 80% of adults have it). I won't include much of the code here because it's so trivial. The bridging process captures packets off the network and pattern matches on the headers:filter(#ether{shost = MAC}, _, #state{mac = MAC}) -> ok; filter(#ether{type = ?ETH_P_IP}, Packet, State) -> {#ipv4{daddr = DA}, _} = epcap_net:ipv4(Packet), filter1(DA, Packet, State); filter(_, _, _) -> ok. filter1(IP, _, #state{ip = IP}) -> ok; filter1(IP, Packet, #state{gw = GW}) -> MAC = case packet:arplookup(IP) of false -> GW; {M1,M2,M3,M4,M5,M6} -> <<M1,M2,M3,M4,M5,M6>> end, bridge(MAC, Packet).
- Check if the frame has our MAC address
- Retrieve the IP address from the frame and check the system ARP cache. A real bridge would monitor the network for ARP packets and cache the results.
- If the IP exists in our ARP cache, use the MAC address, otherwise, send it to the gateway (it's possible the IP address does not exist in the system ARP cache and will be wrongly forwarded to the gateway)
handle_call({packet, DstMAC, Packet}, _From, #state{ mac = MAC, s = Socket, i = Ifindex} = State) -> Ether = epcap_net:ether(#ether{ dhost = DstMAC, shost = MAC, type = ?ETH_P_IP }), packet:send(Socket, Ifindex, list_to_binary([Ether, Packet])), {reply, ok, State};
It'd be interesting to experiment with making herp into a traditional network bridge: quite likely very slow, but also redundant, distributed and fault tolerant.