UDP packet headers from socket() are not as expected - python

I am building a UDP server to parse and verify incoming UDP packets. I am able to receive and parse packets but the header values are not what I expected.
This is structure of incoming packet
Packet ID ( 4 bytes )
Packet Sequence ( 4 bytes )
XOR Key ( 2 bytes )
Number of Checksums in packet ( 2 bytes )
Cyclic checksum CRC32 (variable)
To send the packet,
with open('payloadfile.bin') as op:
payload = pickle.load(op)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for i in payload:
sock.sentto(payload, ('127.0.0.1',4545))
To receive and parse this packet
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind('127.0.0.1',4545)
while 1:
packet = sock.recvfrom(65565)
packet = packet[0]
# parse IP
ip_header = packet[0:20]
iph = struct.unpack('!BBHHHBBH4s4s' , ip_header)
#all the following values are incorrect
version_ihl = iph[0]
version = version_ihl >> 4
ihl = version_ihl & 0xF
ttl = iph[5]
protocol = iph[6]
s_addr = socket.inet_ntoa(iph[8]);
d_addr = socket.inet_ntoa(iph[9]);
# parse UDP
packet = packet[20:28]
data = packet[header_length:]
source_port, dest_port, data_length, checksum = struct.unpack("!HHHH", header)
From what I understand so far, this should be the general structure
IP_HEADER ( UDP_HEADER ( PAYLOAD )))
I want to parse the headers correctly, and then extract the payload.

Unfortunately the standard socket interface doesn't give you access to the data frames that your data arrive in, neither does it include the IP Datagram headers nor the TCP/UDP headers from the transport layer.
To get hold of lower-level data you are forced to use the so-called raw socket interface, which Windows for one tries to block you from using because you might be a hacker. This article might give you some clues.

Related

Capture UDP packets in Python without loss

I'm trying to capture a fast stream of UDP packets without missing any.
Packet size is known and fixed at 1472 bytes.
I'm generating UDP transmissions with another Python application and the first two bytes have a incremental counter so I can check if any have been lost on capture.
Approach 1: read packet -> write to file
Packets are being written to file one at a time as they come in.
Result: 100 to 200 packets lost out of 10,000 received
import socket
import capture_verify
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
address = ("192.168.252.147", 23850)
sock.bind(address)
previous_first_val = 0
out_file = open("out_bin.bin", "wb")
packet_count = 1E5
while(packet_count):
data, addr = sock.recvfrom(2048)
out_file.write(data)
packet_count -= 1
capture_verify.verify_file()
Approach 2: read packets to memory buffer, write to file once 10K captured
Packets are being stored to a pre-allocated buffer. Then written to file once 10K packets received.
Result: 7 to 15 packets lost out of 10,000 received
import socket
import constants as cs
import capture_verify
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
address = ("192.168.252.147", 23850)
sock.bind(address)
out_file = open("out_bin.bin", "wb")
packet_count = 1E5
bytes_to_read = int(cs.packet_size * packet_count)
in_buf = bytearray(bytes_to_read)
view = memoryview(in_buf)
while packet_count:
# nbytes = sock.recv_into(view, bytes_to_read)
sock.recvfrom_into(view, cs.packet_size)
packet_count -= 1
nbytes = cs.packet_size
view = view[nbytes:]
out_file.write(in_buf)
capture_verify.verify_file()
Is there anything I can do to speed up the code and ensure no packet loss?
*UDP protocol is a requirement, can't throttle down transmission speed. Currently using Python 3.7 on Windows 7

Having trouble building a Dns Packet in Python

I'm trying to build a dns packet to send over a socket. I don't want to use any libraries because I want direct access to the socket variable that sends it. Whenever I send the DNS packet, wireshark says that it's malformed. What exactly am I doing wrong?
Some things that are wrong with the Dns packet itself:
It says it has 256 questions, no class and no type
class DnsPacketBuilder:
def __init__(self):
pass
def build_packet(self, url):
packet = struct.pack("H", 12049) # Query Ids (Just 1 for now)
packet += struct.pack("H", 256) # Flags
packet += struct.pack("H", 1) # Questions
packet += struct.pack("H", 0) # Answers
packet += struct.pack("H", 0) # Authorities
packet += struct.pack("H", 0) # Additional
split_url = url.split(".")
for part in split_url:
packet += struct.pack("B", len(part))
for byte in bytes(part):
packet += struct.pack("c", byte)
packet += struct.pack("B", 0) # End of String
packet += struct.pack("H", 1) # Query Type
packet += struct.pack("H", 1) # Query Class
return packet
# Sending the packet
builder = DnsPacketBuilder()
packet = builder.build_packet("www.northeastern.edu")
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('', 8888))
sock.settimeout(2)
sock.sendto(bytes(packet), ("208.67.222.222", 53))
print("Packet Sent")
data, addr = sock.recvfrom(1024)
print("Response: " + data)
sock.close()
Your system is using "little endian" byte order natively.
You need to reverse the byte order of the 16-bit fields into "big endian" (aka "network order") using the ">H" format string in struct.pack().

Python raw udp packet not arriving at udp listener

I'm trying to send a custom UDP packet from a raw socket (On windows if that's relevant) to a udp listener on my VPS, but the packet never shows up at it's destination.
Client:
import struct
import socket
def make_ipv4_header(srcip, dstip, datal, srcprt, dstprt):
srcip = socket.inet_aton(srcip)
dstip = socket.inet_aton(dstip)
ver = 4 #Version 4 for IPv4
ihl = 5 #Header length in 32 bit words. 5 words == 20 bytes
dscp_ecn = 0#Optional fields, don't feel like implementing. Let's keep it at 0
tlen = datal + 28 #Length of data + 20 bytes for ipv4 header + 8 bytes for udp header
ident = socket.htons(54321) #ID of packet
flg_frgoff = 0 #Flags and fragment offset
ttl = 64 #Time to live
ptcl = 17 #Protocol, 17 (UDP)
chksm = 0 #Will automatically fill in checksum
return struct.pack(
"!" #Network(Big endian)
"2B" #Version and IHL, DSCP and ECN
"3H" #Total Length, Identification, Flags and Fragment Offset
"2B" #Time to live, Protocol
"H" #Checksum
"4s" #Source ip
"4s" #Destination ip
, (ver << 4) + ihl, dscp_ecn, tlen, ident, flg_frgoff, ttl, ptcl, chksm, srcip, dstip)
def make_udp_header(srcprt, dstprt, datal):
return struct.pack(
"!4H" #Source port, Destination port, Length, Checksum
, srcprt, dstprt, datal+16, 0)
def makepacket(src, dst, data):
ph = make_ipv4_header(src[0], dst[0], len(data), src[1], dst[1])
uh = make_udp_header(src[1], dst[1], len(data))
return ph+uh+data
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
packet = makepacket(("my ip", 1000), ("vps ip", 10101), "asdf")
s.sendto(packet, ("vps ip", 10101))
Server:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", 10101))
while True:
msg, addr = s.recvfrom(1024)
print msg, addr
I can send a uniform udp packet and it will arrive successfully like so:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto("asdf", ("vps ip", 10101))
Help?
In make_udp_header change the datal from +16 to +8. That seemed to work for me.
change these few lines
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
packet = makepacket(("my ip", 1000), ("vps ip", 10101), "asdf")
s.sendto(packet, ("vps ip", 10101))
By
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
packet = makepacket(("my ip", 1000), ("vps ip", 10101), "asdf")
s.connect(('vps ip',10101))
s.send(packet)
Hopefully this is what you were looking for !!

python-dpkt: ICMP packet parsing

How can I parse a ICMP packet (using dpkt) to check if it is a request or a response coming from A to B?
I found some examples for TCP and UDP packets (below) but I can't find anything for IP packets.
import dpkt
f = open('test.pcap')
pcap = dpkt.pcap.Reader(f)
for ts, buf in pcap:
eth = dpkt.ethernet.Ethernet(buf)
ip = eth.data
tcp = ip.data
if tcp.dport == 80 and len(tcp.data) > 0:
http = dpkt.http.Request(tcp.data)
print http.uri
f.close()
Also, is there any good tutorial for dpkt?
This is an old question but for folks coming across this, I've added an ICMP example to the dpkt repo. The docs can be found here: http://dpkt.readthedocs.io/en/latest/print_icmp.html and the example code can be found in dpkt/examples/print_icmp.py
# For each packet in the pcap process the contents
for timestamp, buf in pcap:
# Unpack the Ethernet frame (mac src/dst, ethertype)
eth = dpkt.ethernet.Ethernet(buf)
# Make sure the Ethernet data contains an IP packet
if not isinstance(eth.data, dpkt.ip.IP):
print 'Non IP Packet type not supported %s\n' % eth.data.__class__.__name__
continue
# Now grab the data within the Ethernet frame (the IP packet)
ip = eth.data
# Now check if this is an ICMP packet
if isinstance(ip.data, dpkt.icmp.ICMP):
icmp = ip.data
# Pull out fragment information
do_not_fragment = bool(ip.off & dpkt.ip.IP_DF)
more_fragments = bool(ip.off & dpkt.ip.IP_MF)
fragment_offset = ip.off & dpkt.ip.IP_OFFMASK
# Print out the info
print 'Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp))
print 'Ethernet Frame: ', mac_addr(eth.src), mac_addr(eth.dst), eth.type
print 'IP: %s -> %s (len=%d ttl=%d DF=%d MF=%d offset=%d)' % \
(inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl,
do_not_fragment, more_fragments, fragment_offset)
print 'ICMP: type:%d code:%d checksum:%d data: %s\n' % (icmp.type,
icmp.code, icmp.sum, repr(icmp.data))
Example Output
Timestamp: 2013-05-30 22:45:17.283187
Ethernet Frame: 60:33:4b:13:c5:58 02:1a:11:f0:c8:3b 2048
IP: 192.168.43.9 -> 8.8.8.8 (len=84 ttl=64 DF=0 MF=0 offset=0)
ICMP: type:8 code:0 checksum:48051 data: Echo(id=55099, data='Q\xa7\xd6}\x00\x04Q\xe4\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567')
Timestamp: 2013-05-30 22:45:17.775391
Ethernet Frame: 02:1a:11:f0:c8:3b 60:33:4b:13:c5:58 2048
IP: 8.8.8.8 -> 192.168.43.9 (len=84 ttl=40 DF=0 MF=0 offset=0)
ICMP: type:0 code:0 checksum:50099 data: Echo(id=55099, data='Q\xa7\xd6}\x00\x04Q\xe4\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567')

Building UDP packet in Python

There's already a code in the internet which shows how to build and send a TCP packet in Python using raw sockets, but I desperately need an example of how building a UDP one.
I read this link http://www.ietf.org/rfc/rfc768.txt and understands that udp header consists only of src ip, src port, length and checksum, and also read that if I create an IPPROTO_UDP socket instead of IPPROTO_RAW socket, the IP header should be filled automatically by the kernel.
Yet, I had no success in doing such.
Here's the code building tcp packets with raw socket:
import socket
import struct
def make_ip(proto, srcip, dstip, ident=54321):
saddr = socket.inet_aton(srcip)
daddr = socket.inet_aton(dstip)
ihl_ver = (4 << 4) | 5
return struct.pack('!BBHHHBBH4s4s' ,
ihl_ver, 0, 0, ident, 0, 255, proto, 0, saddr, daddr)
def make_tcp(srcport, dstport, payload, seq=123, ackseq=0,
fin=False, syn=True, rst=False, psh=False, ack=False, urg=False,
window=5840):
offset_res = (5 << 4) | 0
flags = (fin | (syn << 1) | (rst << 2) |
(psh <<3) | (ack << 4) | (urg << 5))
return struct.pack('!HHLLBBHHH',
srcport, dstport, seq, ackseq, offset_res,
flags, window, 0, 0)
srcip = dstip = '127.0.0.1'
srcport, dstport = 11001, 11000
payload = '[TESTING]\n'
ip = make_ip(socket.IPPROTO_TCP, srcip, dstip)
tcp = make_tcp(srcport, dstport, payload)
packet = ip + tcp + payload
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
s.sendto(packet, (dstip, 0))
response, addr = s.recvfrom(65535)
response_id = struct.unpack('!H', response[4:6])
print response_id
How to make the same thing with UDP instead?
Best solution for me is just editing the existing code, since reading abstract information didn't help me much.
I'm using Python 2.7 on Windows XP.
Just this:
import socket
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((IP,PORT))
To send:
sock.sendto(message,(IP,PORT))
To receive:
sock.recvfrom(1024)
The 1024 displays the buffer size. There are not other changes necessary as far as the socket part

Categories