In Scapy (or even just Python, for that sake), how do I get the size in bytes of a given packet?
I'm tempted to use the function len but I'm not sure what exactly it returns in the case of packets.
>>> len(IP(dst="www.google.com"))
20
>>> len(IP(dst="www.google.com")/TCP(dport=80))
40
>>> len(IP(dst="www.google.com"))
20
There are 20 bytes in a minimal IP header.
>>> len(IP(dst="www.google.com")/TCP(dport=80))
40
There are another 20 bytes in a minimal TCP header (20+20==40).
So it seems that len is returning the packet length.
What I have been observing is that Len(packet[Layer]) will actually perform the action of the LenField type. It will return the number of bytes in the packet, starting with the specified layer, all the way to the end of the packet. So while this method will work for determining the overall packet size, just beware that it will not work to determine the length of an individual layer.
Here is how I grab the packet size/length when sniffing packets with scapy.
pkt.sprintf("%IP.len%")
Full example:
from scapy.all import *
# callback function - called for every packet
def traffic_monitor_callbak(pkt):
if IP in pkt:
print pkt.sprintf("%IP.len%")
# capture traffic for 10 seconds
sniff(iface="eth1", prn=traffic_monitor_callbak, store=0, timeout=10)
I've only used scapy for sniffing packets, so I'm not sure if the above makes sense when using scapy for other things like creating packets.
Related
Can someone help me out, how to simulate IP packet with size smaller than minimum using scapy.
I wanted to reduce the size to 10 to validate an error counter.
Snip from scapy
>>> i=IP(src="20.1.1.2",dst="20.1.1.1")
>>> len(i)
20
I need to reduce this value
Scapy won't let you change the number of bytes in the IP header. What you can do instead is load the raw IP bytes as data on top of the eth payload.
Create the packet
Here we load the IP header as bytes.
>>> ip_data=IP(src="20.1.1.2",dst="20.1.1.1")
>>> raw(ip_data)
b'E\x00\x00\x14\x00\x01\x00\x00#\x00P\xe5\x14\x01\x01\x02\x14\x01\x01\x01'
>>> packet = Ether()/raw(ip_data)
Inspect the packet bytes
We can look at the raw bytes in the packet as an array to look at only the first 10 (or all of the) bytes of the IP header "payload":
>>> packet_bytes = raw(packet)
WARNING: Mac address to reach destination not found. Using broadcast.
>>> eth_boundary = 14
>>> packet_bytes[eth_boundary:] # All IP bytes
b'E\x00\x00\x14\x00\x01\x00\x00#\x00P\xe5\x14\x01\x01\x02\x14\x01\x01\x01'
>>> packet_bytes[eth_boundary:eth_boundary+10] # Only first 10 bytes
b'E\x00\x00\x14\x00\x01\x00\x00#\x00'
In my project I inherited twisted protocol from twisted.internet.protocol import Protocol, As we know, I should do something packet manipulation in the function dataRecieved, However, the doc said:
data: a string of indeterminate length. Please keep in mind that you will probably need to buffer some data, as partial (or multiple) protocol messages may be received! I recommend that unit tests for protocols call through to this method with differing chunk sizes, down to one byte at a time.
So my data format is that:
packet len | payload
2 bytes | variable bytes
But things become complected if I want to encrypt my data, How should I do then?
Should I encrypt both packet length and payload? then how to judge if the packet is end?
Or should I encrypt only the payload, then alter the packet length ? what if the encrypted payload length is larger than the max value of 2bytes?
Plus: If I use raw socket instead of twisted, can I omit the 2 bytes packet len prefix?
Thanks!
I'm currently working on using scapy for sending data packets, and I've run into a weird issue. When I create a packet as such:
pack = IP(dst="127.0.0.1", id=local_ID)/UDP()/chunk
and then convert that packet to a string (so I can send it via a socket)
sendPack = str(pack)
the result of sendPack is wrong.
For instance, in my test file, I have the numbers 1-8000 ordered as such
1
2
3
...
then, when I print("SEND_PACK: "+sendPack)
it produces the following:
E
2
3
...
Everything else is perfect except for the E
I can't understand where that E is coming from, or what it means.
It's also worth noting that I have verified that pack contains the correct data, and that regardless of what the first line of the test file is, the first line of the output is always an E
Thanks!
To those interested, I fixed the issue by doing the following:
As pointed out above, the E was a result of me printing the packet, not it's contents. In order to access the contents I wanted, I had to do the following:
sendPack = pack[UDP].load #Get the packet's load at the UDP layer
id = pack[IP].ID #Get the ID at the IP layer
The documentation for Scapy is sparse, so I didn't realize that I could access the individual fields of each packet this way.
Here's where I found this fix
First, you are printing a packet, not the content of your UDP datagram. The first two fields of an IPv4 packet are version and IHL which values are respectively 4 (for IP version 4) and 5 (5 words of 32 bits) by default in Scapy. This gives 45 if you put it on one byte in hexadecimal which is letter "E" in ascii. This is why you are always seeing an E as the first byte of your string.
If you want to check what is in your packet, you should use Scapy's show() function: sendPack.show().
Second, you could use Scapy's send(), function to send your packet without bothering about sockets yourself: send(sendPack)
I'm creating a networking protocol in application space on top of UDP in python for homework. I need to represent the source port and destination port as 16-bit numbers. All attempts have failed.
The way I'm testing this is by creating a udp socket and looking at the return value of sendto(). Here's your typical socket code:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
addr = ("127.0.0.1", 1234)
Ports range from 0 to 65535. Let's say I choose a port of 65000. I want sendto() to return 2 (2 bytes = 16 bits sent). Here's what I've tried:
I call the following and get:
>>>mySock.sendto(655000, addr)
TypeError: must be string or buffer, not int
Ok, let's try using bytes()
>>>mySock.sendto(bytes(65000), addr)
5
Hm, that's not what I want. That is making each number into a character that is a single byte.
What if I bitwise or it with 0x0000?
>>>mySock.sendto(bytes(65000 | 0x0000), addr)
5
Well, darn it! The closest thing I've come to is messing around with hex() and bytearray(). See below.
>>>hex(65000)
'0xfde8'
>>>mySock.sendto('\xfde8', addr)
3
Shouldn't that say 2 bytes? I'm not sure how this works. Also, when the number is less than 16384 I want to preserve the preceding 0's. So, for example, if the port number is 255 (0b0000000011111111) I want it to remain as a 2 byte data structure (0x00FF) rather than truncating down to 0xFF or 0b11111111.
If you want to send binary data, please use module struct. That will help you to encode the string and to make sure that you are using the proper endianness. For example:
>>> import struct
>>> struct.pack('!H', 65000)
'\xfd\xe8'
That's 65000 as an unsigned short, in network order (big endian)
So I've got a socket like the following:
sock = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.getprotobyname("icmp"))
and when i send out a properly constructed ICMP6 ECHO REQUEST per RFC4443 with type 128 and code 0 (also validated checksum) the packet is dropped by the destination's stack because the packet is malformed, obviously, since the 'next header' byte in the IPv6 header is set to 1 per RFC (# for ICMP).
Two things:
1. I know socket.getprotobyname("icmp") makes the socket ICMP compatible with IPv4 (right?)...
2. Wireshark reads the packet as IPv6 but the protocol as ICMP not ICMPv6...
EITHER set the socket to use protocolbyname("icmpv6") (which is invalid, apparently. unless someone knows the proper string... I've tried "icmp6" "icmpv6" but there's probably some tries with an underscore I could make).
OR change the 'next header' byte before I send the packet to 58.
LAST RESORT construct the packet by hand.
Anyone have an idea? I'm not the most experienced in Python, obviously.
This should do it:
socket.getprotobyname('ipv6-icmp')
For anyone that comes across this,
socket.getprotobyname('<proto>')
returns an integer corresponding to a protocol listed in a flat file in /etc/protocols (Unix) and /c/windows/system32/drivers/etc/protocols (Windows).
Check them out! That's the only byte in an IP header that is specific the the data within its payload. The list on a Unix machine contains ALL the protocols, well, at least on a mac, and Windows contains just the most common. You can edit this file too.