I'm building a traceroute-ish tool to determine the number of hops required for a UDP packet to reach an address using only one probe. To do this, I want to extract the TTL from the ICMP message I receive after sending the probe. I'm doing the following and successfully receiving the ICMP message:
data, source = in_socket.recvfrom(d_bufsize)
But I have no idea how to turn data into something that I can read the TTL from. in_socket is declared like this:
in_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp_proto)
Here, icmp_proto is just the protocol number for ICMP (obtained by doing icmp_proto = socket.getprotobyname("icmp")).
Any help would be much appreciated!
But I have no idea how to turn data into something that I can read
the TTL from.
pyping does it this way:
def header2dict(self, names, struct_format, data):
""" unpack the raw received IP and ICMP header informations to a dict """
unpacked_data = struct.unpack(struct_format, data)
return dict(zip(names, unpacked_data))
…
packet_data, address = current_socket.recvfrom(ICMP_MAX_RECV)
icmp_header = self.header2dict(
names=[
"type", "code", "checksum",
"packet_id", "seq_number"
],
struct_format="!BBHHH",
data=packet_data[20:28]
)
if icmp_header["packet_id"] == self.own_id: # Our packet
ip_header = self.header2dict(
names=[
"version", "type", "length",
"id", "flags", "ttl", "protocol",
"checksum", "src_ip", "dest_ip"
],
struct_format="!BBHHHBBHII",
data=packet_data[:20]
)
packet_size = len(packet_data) - 28
ip = socket.inet_ntoa(struct.pack("!I", ip_header["src_ip"]))
The TTL can then be read from ip_header["ttl"].
Related
I'm trying to code a python script through which I want to send packets from one host (desktop ubuntu) to a server (Ubuntu server) using multiple source addresses. I decided that would be best to use raw sockets and define my own TCP and IP header. I succeed to send the SYN packet and also get a response with the SYN-ACK packet but then my host doesn't respond with the ACK packet but with the RST packet. This is the first problem with I met in my solution. After the beginning of a three-way handshake I would like to send an HTTP GET keep-alive request. Does anyone know how to do it? Or does anyone know a better solution or library to use?
def send_raw_socket():
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
version_ihl_type_of_service = b'\x45\x00'
total_length = b'\x00\x3c'
Indentification = b'\xab\xcd'
flags_fragment_offset = b'\x00\x00'
TTL_protocol = b'\x40\x06'
Dest_add = b'\x0a\x0a'
Dest_add2 = b'\x1e\x03'
Src_add = b'\x0a\x0a'
src_add2 =b'\x0a\x0a'
Header_Checksum = my_ip_header_checksum(version_ihl_type_of_service, total_length, Indentification, flags_fragment_offset, TTL_protocol, Src_add,src_add2, Dest_add, Dest_add2)
IP_protocol = b'\x00\x06'
TCP_header_lenght = b'\x00\x14'
src_port = (int(src_port.hex(), 16)+49607).to_bytes(2, byteorder='big')
dest_port = b'\x1f\x95' # Source Port | Destination Port
seq_n1 = b'\x00\x00'
seq_n2 = b'\x00\x00' # Sequence Number
ack_n1 = b'\x00\x00'
ack_n2 = b'\x00\x00' # Acknowledgement Number
do_res = b'\x50\x02'
flags_win_s = b'\x71\x10' # Data Offset, Reserved, Flags | Window Size
checksum= tcp_header_checksum(IP_protocol, Src_add, src_add2, Dest_add, Dest_add2, TCP_header_lenght, src_port, dest_port, seq_n1, seq_n2, ack_n1, ack_n2, do_res, flags_win_s)
u_pinter= b'\x00\x00' # Checksum | Urgent Pointer
packet = version_ihl_type_of_service+total_length+Indentification+flags_fragment_offset+TTL_protocol+Header_Checksum+Src_add+src_add2+Dest_add+Dest_add2+src_port+dest_port+seq_n1+seq_n2+ack_n1+ack_n2+do_res+flags_win_s+checksum+u_pinter
s.sendto(packet, ('10.10.30.3',8085))
Wireshark three-way handshake
I am reading the packets from a pcap file and inserting some unique id in the TCP payload, So the IP layer length and TCP Sequence and Acknowledge number need to update. Is it possible to do this by scapy?
for pkt in pkt_reader_pcap:
if IP not in pkt:
continue
if TCP not in pkt:
continue
newp = pkt.payload
newp[IP].dst = 1.2.3.4
newp[IP].src = 1.2.3.4
del newp[IP].len
del newp[IP].chksum
# if pkt has TCP layer, insert field before Host to the payload
field = "Id: 1234\r\n"
insert = field + 'Host: '
http_content = newp.getlayer(Raw).load
http_content = http_content.replace(b"Host: ", insert.encode())
newp[Raw].load = http_content
newp[TCP].chksum = None
## manually updated length of IP header which works fine, but it should happen
## automatically or by some method of scapy
newp[IP].len += len(field)
I set the TCP/IP checksum to None, but still, the Seq/Ack number is not updated. Is there any way to achieve the same?
In l3_learning.py, there is a method in class l3_switch named _handle_PacketIn.
Now I understand that this event is when a switch contacts controller when it receives a packet corresponding to which it has no entries in its table.
What I don't understand is that here
packet = event.parsed
Now what does packet.next mean in isinstance(packet.next, ipv4)?
def _handle_PacketIn (self, event):
dpid = event.connection.dpid
inport = event.port
packet = event.parsed
if not packet.parsed:
log.warning("%i %i ignoring unparsed packet", dpid, inport)
return
if dpid not in self.arpTable:
# New switch -- create an empty table
self.arpTable[dpid] = {}
for fake in self.fakeways:
self.arpTable[dpid][IPAddr(fake)] = Entry(of.OFPP_NONE,
dpid_to_mac(dpid))
if packet.type == ethernet.LLDP_TYPE:
# Ignore LLDP packets
return
if isinstance(packet.next, ipv4):
log.debug("%i %i IP %s => %s", dpid,inport,
packet.next.srcip,packet.next.dstip)
# Send any waiting packets...
self._send_lost_buffers(dpid, packet.next.srcip, packet.src, inport)
# Learn or update port/MAC info
if packet.next.srcip in self.arpTable[dpid]:
if self.arpTable[dpid][packet.next.srcip] != (inport, packet.src):
log.info("%i %i RE-learned %s", dpid,inport,packet.next.srcip)
else:
log.debug("%i %i learned %s", dpid,inport,str(packet.next.srcip))
self.arpTable[dpid][packet.next.srcip] = Entry(inport, packet.src)
# Try to forward
dstaddr = packet.next.dstip
if dstaddr in self.arpTable[dpid]:
# We have info about what port to send it out on...
prt = self.arpTable[dpid][dstaddr].port
mac = self.arpTable[dpid][dstaddr].mac
I think I have figured it out.
packet is entire packet that data-link layer sends to the physical layer. packet.next removes the encapsulation of data-link layer and reveals the IP packet (the packet sent by IP layer to data-link layer). So to get the source MAC address we use packet.src and to get the IP address of the source we use packet.next.srcip
I am trying to measure the responses back from DNS servers. Making a sniffer for a typical DNS response that is less than 512 bytes is no big deal. My issue is receiving large 3000+ byte responses - in some cases 5000+ bytes. I haven't been able to get a socket working that can receive that data reliably. Is there a way with Python sockets to receive from a specific source address?
Here is what I have so far:
import socket
import struct
def craft_dns(Qdns):
iden = struct.pack('!H', randint(0, 65535))
QR_thru_RD = chr(int('00000001', 2)) # '\x01'
RA_thru_RCode = chr(int('00100000', 2)) # '\x00'
Qcount = '\x00\x01' # question count is 1
ANcount = '\x00\x00'
NScount = '\x00\x00'
ARcount = '\x00\x01' # additional resource count is 1
pad = '\x00' #
Rtype_ANY = '\x00\xff' # Request ANY record
PROtype = '\x00\x01' # Protocol IN || '\x00\xff' # Protocol ANY
DNSsec_do = chr(int('10000000', 2)) # flips DNSsec bit to enable
edns0 = '\x00\x00\x29\x10\x00\x00\x00\x00\x00\x00\x00' # DNSsec disabled
domain = Qdns.split('.')
quest = ''
for x in domain:
quest += struct.pack('!B', len(x)) + x
packet = (iden+QR_thru_RD+RA_thru_RCode+Qcount+ANcount+NScount+ARcount+
quest+pad+Rtype_ANY+PROtype+edns0) # remove pad if asking <root>
return packet
def craft_ip(target, resolv):
ip_ver_len = int('01000101', 2) # IPvers: 4, 0100 | IP_hdr len: 5, 0101 = 69
ipvers = 4
ip_tos = 0
ip_len = 0 # socket will put in the right length
iden = randint(0, 65535)
ip_frag = 0 # off
ttl = 255
ip_proto = socket.IPPROTO_UDP # dns, brah
chksm = 0 # socket will do the checksum
s_addr = socket.inet_aton(target)
d_addr = socket.inet_aton(resolv)
ip_hdr = struct.pack('!BBHHHBBH4s4s', ip_ver_len, ip_tos, ip_len, iden,
ip_frag, ttl, ip_proto, chksm, s_addr, d_addr)
return ip_hdr
def craft_udp(sport, dest_port, packet):
#sport = randint(0, 65535) # not recommended to do a random port generation
udp_len = 8 + len(packet) # calculate length of UDP frame in bytes.
chksm = 0 # socket fills in
udp_hdr = struct.pack('!HHHH', sport, dest_port, udp_len, chksm)
return udp_hdr
def get_len(resolv, domain):
target = "10.0.0.3"
d_port = 53
s_port = 5353
ip_hdr = craft_ip(target, resolv)
dns_payload = craft_dns(domain) # '\x00' for root
udp_hdr = craft_udp(s_port, d_port, dns_payload)
packet = ip_hdr + udp_hdr + dns_payload
buf = bytearray("-" * 60000)
recvSock = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0800))
recvSock.settimeout(1)
sendSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
sendSock.settimeout(1)
sendSock.connect((resolv, d_port))
sendSock.send(packet)
msglen = 0
while True:
try:
pkt = recvSock.recvfrom(65535)
msglen += len(pkt[0])
print repr(pkt[0])
except socket.timeout as e:
break
sendSock.close()
recvSock.close()
return msglen
result = get_len('75.75.75.75', 'isc.org')
print result
For some reason doing
pkt = sendSock.recvfrom(65535)
Recieves nothing at all. Since I'm using SOCK_RAW the above code is less than ideal, but it works - sort of. If the socket is extremely noisy (like on a WLAN), I could end up receiving well beyond the DNS packets, because I have no way to know when to stop receiving packets when receiving a multipacket DNS answer. For a quiet network, like a lab VM, it works.
Is there a better way to use a receiving socket in this case?
Obviously from the code, I'm not that strong with Python sockets.
I have to send with SOCK_RAW because I am constructing the packet in a raw format. If I use SOCK_DGRAM the custom packet will be malformed when sending to a DNS resolver.
The only way I could see is to use the raw sockets receiver (recvSock.recv or recvfrom) and unpack each packet, look if the source and dest address match within what is supplied in get_len(), then look to see if the fragment bit is flipped. Then record the byte length of each packet with len(). I'd rather not do that. It just seems there is a better way.
Ok I was stupid and didn't look at the protocol for the receiving socket. Socket gets kind of flaky when you try to receive packets on a IPPROTO_RAW protocol, so we do need two sockets. By changing to IPPROTO_UDP and then binding it, the socket was able to follow the complete DNS response over multiple requests. I got rid of the try/catch and the while loop, as it was no longer necessary and I'm able to pull the response length with this block:
recvSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
recvSock.settimeout(.3)
recvSock.bind((target, s_port))
sendSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
#sendSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sendSock.settimeout(.3)
sendSock.bind((target, s_port))
sendSock.connect((resolv, d_port))
sendSock.send(packet)
pkt = recvSock.recvfrom(65535)
msglen = len(pkt[0])
Now the method will return the exact bytes received from a DNS query. I'll leave this up in case anyone else needs to do something similar :)
I'd need build up an UDP packet with Python setting a particular value of its TTL. Could anyone show me the minimal-length code to do this?
Using PyIP.
Not tested, but shows the idea:
import ip
import udp
import socket
# build UDP
udp_packet = udp.Packet()
udp_packet.sport = 1024;
udp_packet.dport = 3024;
udp_packet.data = "\xFF\xFF\xFF\xFFrcon \"test\" test\0"
udp_data = udp.assemble(udp_packet, 0)
# build IP packet
ip_packet = ip.Packet()
ip_packet.src = "1.1.1.1"
ip_packet.dst = "2.2.2.2"
ip_packet.ttl = 10
ip_packet.data = udp_data
packet = ip.assemble(ip_packet, 0)
# send the packet here