I'm trying to write a simple python server that accepts a connection from whatever and digests the entire frame, down to the ethernet headers. The server will not maintain a connection or care about the client once it receives the frame. I'm sort of going for a frame visualizer, think wireshark, but the server creates that visualization, which isn't a part of this question.
If I use a "traditional" AF_INET/SOCK_STREAM
socket for this server, the only data I receive from clients will be the data payload itself. With an AF_INET/SOCK_RAW
socket, I will get Layer 3 headers, Layer 4 headers, and the data. See this question for a better explanation on that.
With an AF_PACKET/SOCK_RAW
socket, I would in theory get all the rest of the goodies down to the ethernet headers and be able to receive those, and digest them to my heart's content. The problem, I think, is that when using SOCK_RAW
you're essentially telling your NIC that you don't want to handle any of the setup of the "connection". This is a problem because I want a client to be able to send TCP data / open a connection (or UDP or whatever else), have the server capture that request(s), and then do whatever with the ethernet headers the client sends to be used in a visualization. For now I'm content with one frame at a time, but I'd like to be able to do multiple in a series at a later date, which means I need connections to work.
Here's the issue: With a SOCK_RAW
, the server would also need to handle the entire TCP handshake, or any other process that happens. That's out of scope for this project and I really just want to capture the entire frame of a request, I'm not trying to reinvent the wheel. Is AF_PACKET/SOCK_RAW
what I'm actually looking for, or is there another way to capture the entire L2->L7 request?
Here's an example of a would-be client.py, sending basic data over a TCP socket:
import socket
HOST = '192.168.0.95'
PORT = 5000
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
Predictably, this works for a normal echo server. Here's the server.py, which I've been toying with and is otherwise garbage code. I would like to get the whole OSI onion from the client above, for example.
server.py
import socket
PORT = 5000
ETH_P_ALL = 3
ETH_P_IP = 0x800 # These two were stolen from some other answer, not sure
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL))
s.bind(('wlp0s20f3', 5000)) # Bind RAW socket to this specific interface, I guess
# Since raw sockets bind on an interface and not an address
# Note: Loopback (lo) usually gave me broken pipe / connection refused errors,
# this one seems to work (read: not break)
while True:
print(s.recvfrom(4096))
The server sits patiently and waits for incoming data, which it never receives. The client, upon executing, gets a ConnectionRefusedError: [Errno 111] Connection refused
.
I have tried getting some data on the wire with a client AF_PACKET/SOCK_RAW
socket instead of a TCP socket, and while it does get on the wire and is visible with wireshark (unlike the simple client above), the server still doesn't see the data or receive anything at all.