0

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.

CD1210
  • 1

1 Answers1

0

... 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.

You cannot have both on the same socket: either you make use of the OS implementation of TCP, which abstracts away all the lower layers since they are not needed for the application. Or you want to get access to the raw packets.

If you want to have both you need to have two sockets: one for the raw packets and one for the TCP abstraction and the do the mapping between these in your code. But this will not be simple and there is no 1:1 relation between the data you receive on both sockets: A send/recv in TCP does not map to a single raw packet but can actually be spread over multiple packets or be only a part of a packet.

Another way would be to only use the raw socket and use a user space TCP implementation, i.e. bypass the implementation in the OS.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172