Identifying connected Device Router on Local Area Network using Python - python

So I have 4 routers and 1 PC with 4 devices that connect to the closest router. So What I want to do is write a script in python that is running on the PC (Windows) and that PC is connected to 1 main router and that router connects to the other 3 routers.
1 PC -> Main Router (WiFi Enabled) -> 3 Other Routers (WiFi Enabled)
The bracelet (a WiFi enabled device) will connect to the nearest WiFi Router available of the 4 routers. I want to detect to which router is that bracelet connected to via a Python script.
I can make the Bracelet's IP Address Static as well but I was hoping to identify them using the MAC Address.
I can edit the routers configuration via web-page if needed.
Do ask for any clarifications if needed.
Hoping to hear soon.
Edit_1: Not to be confused with me asking for the whole code, I am in search of the right functions that can be used for this process and what are the possible configurations that I should set the router to.
Edit_2: Below is a sample code which allows for pinging IP's. So how can I get their MAC address (function) and also ping to other access points? I can alter properties of the routers.
import socket
from datetime import datetime
net = input("Enter the IP address: ") #e.g. is 192.168.18.1
net1 = net.split('.')
a = '.'
net2 = net1[0] + a + net1[1] + a + net1[2] + a
st1 = int(input("Enter the Starting Number: ")) #e.g. 1
en1 = int(input("Enter the Last Number: ")) #e.g. 10
en1 = en1 + 1
t1 = datetime.now()
def scan(addr):
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket.setdefaulttimeout(1)
result = s.connect_ex((addr,135))
if result == 0:
return 1
else :
return 0
def run1():
for ip in range(st1,en1):
addr = net2 + str(ip)
if (scan(addr)):
print (addr , "is live")
run1()
t2 = datetime.now()
total = t2 - t1
print ("Scanning completed in: " , total)
Edit_3: This searches out the MAC Address as well.
from scapy.all import ARP, Ether, srp
target_ip = "192.168.18.1/24"
# IP Address for the destination
# create ARP packet
arp = ARP(pdst=target_ip)
# create the Ether broadcast packet
# ff:ff:ff:ff:ff:ff MAC address indicates broadcasting
ether = Ether(dst="ff:ff:ff:ff:ff:ff")
# stack them
packet = ether/arp
result = srp(packet, timeout=3, verbose=0)[0]
# a list of clients, we will fill this in the upcoming loop
clients = []
for sent, received in result:
# for each response, append ip and mac address to `clients` list
clients.append({'ip': received.psrc, 'mac': received.hwsrc})
# print clients
print("Available devices in the network:")
print("IP" + " "*18+"MAC")
for client in clients:
print("{:16} {}".format(client['ip'], client['mac']))
if (client['mac'] == "f4:f5:e8:37:67:92"): #Testing mac address filter
print("Success")

Related

Python sending three-way handshake raw socket

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

Network Scanner build using Scapy only scans itself

Below is the code of NetworkScanner that I tried to build as my first Python project.
#!/usr/bin/env python
import scapy.all as scapy
import optparse
def scan(ip):
packet1 = scapy.ARP(pdst=ip)
etherpacket = scapy.Ether(dst='ff:ff:ff:ff:ff:ff')
broadcast_packet = etherpacket / packet1
ans = scapy.srp(broadcast_packet, timeout=60, verbose=False)[0]
ret_list = list()
for item in ans:
dic = {}
dic['ip'] = item[1].pdst
dic['mac'] = item[1].hwdst
ret_list.append(dic)
print(ret_list)
return ret_list
def printfun(returnlist):
print("IP\t\t\tMAC Address\n----------------------------------------------")
for elem in returnlist:
print(elem["ip"] + "\t\t" + elem["mac"])
def getip():
parser = optparse.OptionParser()
parser.add_option('-i', "--ip", dest = 'received_ip', help="Please enter the ip you want to scan")
(option, arguments) = parser.parse_args()
return option.received_ip
ip = getip()
if ip:
result = scan(ip)
printfun(result)
else:
print("no ip given")
Now I did follow some tutorials and learned to build parallelly and it seems right to me but I am not good.
but when I execute the program, it only scans the IP of virtual Host itself on which it is executed.
/PycharmProjects/Networkscanner$ sudo python networkscanner.py -i 192.168.1.1/24
[{'ip': '192.168.1.205', 'mac': '08:00:27:1f:30:76'}, {'ip': '192.168.1.205', 'mac': '08:00:27:1f:30:76'}]
IP MAC Address
----------------------------------------------
192.168.1.205 08:00:27:1f:30:76
192.168.1.205 08:00:27:1f:30:76
when I use the inbuild network scanner of python it gives these results.
Currently scanning: Finished! | Screen View: Unique Hosts
5 Captured ARP Req/Rep packets, from 4 hosts. Total size: 300
_____________________________________________________________________________
IP At MAC Address Count Len MAC Vendor / Hostname
-----------------------------------------------------------------------------
192.168.1.1 a0:47:d7:36:2a:c0 2 120 Best IT World (India) Pvt Lt
192.168.1.203 e4:42:a6:30:93:64 1 60 Intel Corporate
192.168.1.205 30:b5:c2:10:05:3b 1 60 TP-LINK TECHNOLOGIES CO.,LTD
192.168.1.207 30:b5:c2:10:05:3b 1 60 TP-LINK TECHNOLOGIES CO.,LTD
Edit:
I tried the Monitor mode, but that does not help.
I also tried to run it on main windows with also an external WiFi adaptor, still same issue
can someone please assist what is wrong in my code?
Determining the problem
If we name variables more appropriately, it becomes apparent what the problem is:
for sent_recv in ans:
dic = {}
return_packet = sent_recv[1]
dic['ip'] = return_packet.pdst
dic['mac'] = return_packet.hwdst
ret_list.append(dic)
Each item is a tuple of a sent packet and a received packet. Naming it as such helps to identify it.
The 2nd element of sent_recv is the returned packet. Let's name it as such.
The destination IP and MAC address of the returning packet are going to be that of our machine. This makes sense in the context of your output: You get your own IP/MAC for every response.
So if we change it to the src IP/MAC of the returning packet, we'll get the information we're after:
for sent_recv in ans:
dic = {}
return_packet = sent_recv[1]
# Difference is dst => src here
dic['ip'] = return_packet.psrc
dic['mac'] = return_packet.hwsrc
ret_list.append(dic)
Note: An ARP packet should not take 60s to return - it's more on the scale of 10-100ms depending on network size. A timeout of 2s is more appropriate here.
Testing the fix
Running this with modified code, we get the desired result:
$ python script.py -i "192.168.1.0/24"
[{'ip': '192.168.1.48', 'mac': '00:1b:78:20:ee:40'},
{'ip': '192.168.1.67', 'mac': '6c:33:a9:42:6a:18'},
...
IP MAC Address
----------------------------------------------
192.168.1.48 00:1b:78:20:ee:40
192.168.1.67 6c:33:a9:42:6a:18
...

How can I separate two sockets that receive two multicasts on the same port?

I created a program, that receives multicast stream and analyzes its bitrate.
The problem started, when I wanted to use multithreading approach here, in order to analyze many multicasts at the same time. Every multicast has it's unique addres, however they have the same port. First stream: 239.0.1.104 has constant bitrate of 10.69 Mbps, second: 239.0.1.105 has also CBR of 6.082 Mbps. The problem is, my program sums up results, and at the end i have:
16.328928
16.328928
16.802688
16.802688
16.750048
16.750048
16.813216
16.813216
16.771104
16.771104
Whereas I expect:
10.69
6.082
10.69
6.082
etc
Important:
I use socket option SO_REUSEADDR which in case of multicast is equal to using both SO_REUSEADDR and SO_REUSEPORT.(Mentioned few lines below)
I read article:
https://lwn.net/Articles/542629/
And also(very compact information providing):
How do SO_REUSEADDR and SO_REUSEPORT differ?
Where i read:
The meaning of SO_REUSEADDR changes for multicast addresses as it allows multiple sockets to be bound to exactly the same combination of source multicast address and port. In other words, for multicast addresses SO_REUSEADDR behaves exactly as SO_REUSEPORT for unicast addresses. Actually, the code treats SO_REUSEADDR and SO_REUSEPORT identically for multicast addresses, that means you could say that SO_REUSEADDR implies SO_REUSEPORT for all multicast addresses and the other way round.
I tried providing socket_name as parameter in order to distinguish both sockets, I tried also to add bitrateList_name and totalSize name as parameters, but always there is the same problem.
import socket
import struct
import time
import threading
from collections import Counter
MCAST_GRP = ['239.0.1.104','239.0.1.105']
MCAST_PORT1 = 12345
MCAST_PORT2 = 12345
def mcanalysis(multicast_group, MCAST_PORT):
IS_ALL_GROUPS = True
#scan time in seconds
SCAN_TIME = 5
#sampling time in seconds
SAMPLING_TIME = 1
bufferUDP = 2048
totalSize = 0
bitrateList = []
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
# on this port, receives ALL multicast groups
sock.bind(('', MCAST_PORT))
else:
# on this port, listen ONLY to MCAST_GRP
sock.bind((multicast_group, MCAST_PORT))
#Creating socket that gets UDP multicast packets
for group in MCAST_GRP:
mreq = struct.pack("4sl", socket.inet_aton(group), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
print("_____.:| Starting analysis of multicasts! |:._____\n")
print("͞◌͞◌͞◌͞◌͞.:| IP: {} PORT: {} |:.͞◌͞◌͞◌͞͞◌͞◌".format(multicast_group,MCAST_PORT))
SCAN_TIME = int(SCAN_TIME *(SAMPLING_TIME**(-1)))
for x in range(SCAN_TIME):
stop = time.time() + SAMPLING_TIME
while (time.time()<stop):
data, address = sock.recvfrom(bufferUDP)
totalSize += len(data)
bitrateList.append(totalSize)
print(bitrateList[x]*8/(1000000*SAMPLING_TIME))
totalSize = 0
bitrateList.pop(0)
txtfile = open("Bitrate_history_ip_{}.txt".format(multicast_group),"w+")
for x in range(SCAN_TIME-1):
bitrateList[x] = bitrateList[x]*8/(1000000*SAMPLING_TIME)
txtfile.write("{}.Bitrate was equal to: {} Mbps\n".format(x+1,bitrateList[x]))
txtfile.write("Maximum bitrate value was: {} Mbps\n".format(max(bitrateList)))
txtfile.write("Minimum bitrate value was: {} Mbps\n".format(min(bitrateList)))
t1 = threading.Thread(target=mcanalysis, args=(MCAST_GRP[0],MCAST_PORT1))
t2 = threading.Thread(target=mcanalysis, args=(MCAST_GRP[1],MCAST_PORT2))
t1.start()
t2.start()
t1.join()
t2.join()
print('End of test')
time.sleep(5)
I would to thank in advance for any information that can bring me closer to resolving this problem

How to detect SSTP traffic?

I'm writing a program which should detect a VPN traffic.
As far as I read about the subject, it seems the detection of the tunneling protocol is easy as firewall rules, using their dedicated ports:
PPTP: port 1723/TCP
OpenVPN: port 1194
L2TP: port 1701/UDP
My problem is with the SSTP, because it is using port 443, which is widely used.
So I have 2 questions:
Am I too naive to think I can detect those VPNs tunneling protocols only by their ports?
Does anyone has any idea how to detect SSTP and differ its traffic from any other type / app which uses TLS/SSL (even using DPI)?
I'm attaching piece of Python code which detects the communication in the above ports
import dpkt
import socket
# -------------------------- Globals
# VPN PORTS
import common
import dal
protocols_strs = {"pp2e_gre": "1723/TCP PP2P_GRE_PORT",
"openvpn": "1194 OPENVPN_PORT",
"ike": "500/UDP IKE_PORT",
"l2tp_ipsec": "1701/UDP L2TP_IPSEC_PORT"
}
port_protocols = {1723: 'pp2e_gre',
1194: 'openvpn',
500: 'ike',
1701: 'l2tp_ipsec'
}
# Dict of sets holding the protocols sessions
protocol_sessions = {"pp2e_gre": [],
"openvpn": [],
"ike": [],
"l2tp_ipsec": []}
# -------------------------- Functions
def is_bidirectional(five_tuple, protocol):
"""
Given a tuple and protocol check if the connection is bidirectional in the protocol
:param five_tuple:
:return: True of the connection is bidirectional False otherwise
"""
src_ip = five_tuple['src_ip']
dest_ip = five_tuple['dest_ip']
# Filter the sessions the five tuple's ips spoke in
ike_sessions = filter(lambda session: (session['src_ip'] == src_ip and session['dest_ip'] == dest_ip)
or
(session['dest_ip'] == src_ip and session['src_ip'] == dest_ip),
protocol_sessions[protocol])
# Return true if 2 session (1 for each direction) were found
return len(ike_sessions) == 2
def print_alert(timestamp, protocol, five_tuple):
"""
Print alert description to std
:param timestamp:
:param protocol:
:param five_tuple:
:return:
"""
print timestamp, ":\t detected port %s communication (%s:%s ---> %s:%s)" % \
(protocol, five_tuple['src_ip'], five_tuple['src_port'], five_tuple['dest_ip'],
five_tuple['dest_port'])
def pp2e_gre_openvpn_ike_handler(five_tuple):
# Get protocol
protocol = five_tuple['protocol']
# Clear old sessions in db
dal.remove_old_sessions(five_tuple['timestamp'], 'vpn_sessions')
# Clear old sessions in cache
protocol_sessions[protocol] = common.clear_old_sessions(five_tuple, protocol_sessions[protocol])
# If session already exists - return
if common.check_if_session_exists(five_tuple, protocol_sessions[protocol]):
session_to_update = common.get_session(five_tuple, protocol_sessions[protocol])
session_to_update['timestamp'] = five_tuple['timestamp']
return
# Update DB
dal.upsert_vpn_session(five_tuple)
# Add to cache
protocol_sessions[protocol].append(five_tuple)
# Print alert
print_alert(five_tuple['timestamp'], protocols_strs[protocol], five_tuple)
def l2tp_ipsec_handler(five_tuple):
if five_tuple in protocol_sessions['l2tp_ipsec']:
return
# If bi-directional IKE protocol performed earlier - alert
if not is_bidirectional(five_tuple, 'ike'):
return
protocol_sessions['l2tp_ipsec'].append(five_tuple)
print_alert(five_tuple['timestamp'], protocols_strs['l2tp_ipsec'], five_tuple)
# -------------------------- VPN ports jump tables
tcp_vpn_ports = {1723: pp2e_gre_openvpn_ike_handler,
1194: pp2e_gre_openvpn_ike_handler}
udp_vpn_ports = {500: pp2e_gre_openvpn_ike_handler,
1701: l2tp_ipsec_handler,
1194: pp2e_gre_openvpn_ike_handler}
# -------------------------- Functions
def process_packet(timestamp, packet):
"""
Given a packet process it for detecting a VPN communication
:param packet:
:param timestamp:
:return:
"""
# Parse the input
eth_frame = dpkt.ethernet.Ethernet(packet)
# Check if IP
if eth_frame.type != dpkt.ethernet.ETH_TYPE_IP:
return
# If not IP return
ip_frame = eth_frame.data
# if TCP or UDP
if ip_frame.p not in (dpkt.ip.IP_PROTO_TCP, dpkt.ip.IP_PROTO_UDP):
return
# Extract L3 frame
frame = ip_frame.data
# Extract ports
frame_ports = (frame.sport, frame.dport)
# get VPN ports in session
detected_ports = set(tcp_vpn_ports).intersection(frame_ports)
# If TCP VPN port was not detected return
if not detected_ports:
return
# Get detected port
port = detected_ports.pop()
# Translate port to str
protocol_str = port_protocols[port]
# Choose handler by port
handler = tcp_vpn_ports[port]
# Extract 5-tuple parameters from frames
five_tuple = {'src_ip': socket.inet_ntoa(ip_frame.src),
'dest_ip': socket.inet_ntoa(ip_frame.dst),
'src_port': frame.sport,
'dest_port': frame.dport,
'protocol': protocol_str,
'timestamp': timestamp}
# Invoke the chosen handler
handler(five_tuple)
"Am I too naive to think I can detect those VPNs tunneling protocols only by their ports?:
"The official OpenVPN port number is 1194, but any port number between 1 and 65535 will work. If you don't provide the 'port' option, 1194 will be used.
So if your code is looking for 1194 traffic, as per the dictionary entries, you will capture default Open VPN flows only.
The SSTP message is encrypted with the SSL channel of the HTTPS protocol. So I don't see how you would identify this traffic as it is encrypted. (Source)

POX in mininet: What does event.parsed give in pox? What is parse.next?

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

Categories