Initial commit
This commit is contained in:
commit
4be9f67e63
8 changed files with 184 additions and 0 deletions
1
.env
Normal file
1
.env
Normal file
|
@ -0,0 +1 @@
|
||||||
|
INTERFACE=wlp58s0
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
venv/
|
||||||
|
__pycache__/
|
8
Makefile
Normal file
8
Makefile
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#!make
|
||||||
|
include .env
|
||||||
|
export
|
||||||
|
|
||||||
|
listener:
|
||||||
|
python listener.py
|
||||||
|
example-sender:
|
||||||
|
python python/sender.py
|
37
README.md
Normal file
37
README.md
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# Raw socket programming challenge
|
||||||
|
|
||||||
|
How to send a pure ethernet packet?
|
||||||
|
|
||||||
|
Let us know in your favorite programming language!
|
||||||
|
|
||||||
|
|
||||||
|
## What are we expecting?
|
||||||
|
|
||||||
|
A layer 2 ethernet frame is constructed in the following way.
|
||||||
|
|
||||||
|
```
|
||||||
|
[Destination MAC: 6 bytes][Source MAC: 6 bytes][Protocol: 2 bytes][Message: ZeusWPI is de max!]
|
||||||
|
```
|
||||||
|
For example
|
||||||
|
```python
|
||||||
|
\xff\xff\xff\xff\xff\xff \x12\xab\x34\xcd\x56 \x60\x00 \x5a\x45\x75\x53...
|
||||||
|
Broadcast Dest MAC | Source MAC | Protocol | Your binary message
|
||||||
|
```
|
||||||
|
|
||||||
|
Send that frame with the message `ZeusWPI is de max!` over the same network interface that the listener is listening on and it will confirm that you completed the challenge!
|
||||||
|
|
||||||
|
You can then put your code in a folder with the name of the programming language providing instructions on how to run it.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## How to
|
||||||
|
We are providing some code that will listen to any packets received. It will give you a confirmation if you completed the challenge.
|
||||||
|
|
||||||
|
The only python package requirement is `netifaces`.
|
||||||
|
Install with the command `pip install --user netifaces`
|
||||||
|
|
||||||
|
Create a file named `.env` with the following line `INTERFACE=YOUR_NETWORK_INTERFACE`
|
||||||
|
|
||||||
|
* Run the listener with `sudo make listener`
|
||||||
|
|
||||||
|
* We also provide an example client in python. Run it with `sudo make example-sender`
|
64
listener.py
Normal file
64
listener.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import datetime
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from utils import mac_address, broadcast_address
|
||||||
|
|
||||||
|
|
||||||
|
class Frame:
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
destination: bytes,
|
||||||
|
protocol: bytes,
|
||||||
|
message: str,
|
||||||
|
source: bytes = mac_address):
|
||||||
|
self.destination: bytes = destination
|
||||||
|
self.source: bytes = source
|
||||||
|
self.protocol: bytes = protocol
|
||||||
|
self.message: str = message
|
||||||
|
|
||||||
|
|
||||||
|
def decode_packet(packet_bytes: bytes) -> Optional[Frame]:
|
||||||
|
protocol: bytes = packet_bytes[12:14]
|
||||||
|
|
||||||
|
if protocol != b"\x60\x00":
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
destination = packet_bytes[0:6]
|
||||||
|
source = packet_bytes[6:12]
|
||||||
|
message = packet_bytes[14:].decode("utf-8").rstrip("\x00")
|
||||||
|
return Frame(destination=destination,
|
||||||
|
source=source,
|
||||||
|
protocol=protocol,
|
||||||
|
message=message)
|
||||||
|
except:
|
||||||
|
# print("[Error] Could not decode message.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
print("Starting listener...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
connection = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(3))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
byte_string, addr = connection.recvfrom(65536)
|
||||||
|
|
||||||
|
frame = decode_packet(byte_string)
|
||||||
|
|
||||||
|
if frame is not None \
|
||||||
|
and (frame.destination == mac_address or frame.destination == broadcast_address):
|
||||||
|
dt_string = datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")
|
||||||
|
print("----")
|
||||||
|
print("Revcd from {} to {} @{}".format(frame.source, frame.destination, dt_string))
|
||||||
|
|
||||||
|
if frame.message == "ZeusWPI is de max!":
|
||||||
|
print("Congratulations, packet received correctly!")
|
||||||
|
else:
|
||||||
|
print("Wrong message, please check that you are sending \"ZeusWPI is de max!\"")
|
||||||
|
|
||||||
|
sys.stdout.flush()
|
47
python/sender.py
Normal file
47
python/sender.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import datetime
|
||||||
|
from _socket import socket, AF_PACKET, SOCK_RAW
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
import utils
|
||||||
|
|
||||||
|
|
||||||
|
class Frame:
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
destination: bytes,
|
||||||
|
protocol: bytes,
|
||||||
|
message: str,
|
||||||
|
source: bytes = utils.mac_address):
|
||||||
|
self.destination: bytes = destination
|
||||||
|
self.source: bytes = source
|
||||||
|
self.protocol: bytes = protocol
|
||||||
|
self.message: str = message
|
||||||
|
|
||||||
|
|
||||||
|
def send_packet(packet: Frame) -> bool:
|
||||||
|
sock = socket(AF_PACKET, SOCK_RAW)
|
||||||
|
|
||||||
|
encoded = packet.message.encode()
|
||||||
|
sock.bind((utils.get_device_name(), 0))
|
||||||
|
|
||||||
|
"""
|
||||||
|
The way ethernet works is:
|
||||||
|
- 6 bytes indicating destination mac
|
||||||
|
- 6 bytes indicating source mac
|
||||||
|
- 2 bytes indicating the type
|
||||||
|
- maximum 1500 bytes of data to send
|
||||||
|
"""
|
||||||
|
data = (packet.destination + utils.mac_address +
|
||||||
|
packet.protocol + encoded)
|
||||||
|
dt_string = datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")
|
||||||
|
print("Sending message to {} @{}: {}".format(packet.destination, dt_string, packet.message))
|
||||||
|
sock.send(data)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
while True:
|
||||||
|
send_packet(Frame(destination=utils.broadcast_address,
|
||||||
|
protocol=b"\x60\x00",
|
||||||
|
message="ZeusWPI is de max!"))
|
||||||
|
sleep(1)
|
1
python/utils.py
Symbolic link
1
python/utils.py
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../utils.py
|
22
utils.py
Normal file
22
utils.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
import netifaces
|
||||||
|
|
||||||
|
|
||||||
|
def get_device_name() -> str:
|
||||||
|
if 'INTERFACE' not in os.environ:
|
||||||
|
print("[Error] \"INTERFACE\" not found in your environment variables")
|
||||||
|
print(" Please specify your network interface")
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
return os.environ['INTERFACE']
|
||||||
|
|
||||||
|
|
||||||
|
def get_mac_address() -> bytes:
|
||||||
|
return bytes(list(map(lambda i: int(i, 16), netifaces.ifaddresses(
|
||||||
|
get_device_name())[netifaces.AF_LINK][0]["addr"].split(":"))))
|
||||||
|
|
||||||
|
|
||||||
|
mac_address = get_mac_address()
|
||||||
|
|
||||||
|
broadcast_address = b'\xff\xff\xff\xff\xff\xff'
|
Loading…
Reference in a new issue