Socket recvfrom function hanging, not recognizing ICMP packet - sockets

So I'm trying to do a mock Traceroute function where a UDP packet is sent to an IP address. I'm trying to design the program in such a way where a packet is sent each time the packet makes it to a router. I am trying to do this by making a very short TTL. However, the recvfrom function is stalling.
Here's the code:
host_addr = gethostbyname(host)
port = 33434
max_hops = 30
ttl = 1
while True:
recv_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
send_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
send_socket.setsockopt(0, 4, ttl)
recv_socket.bind(("", port))
send_socket.sendto("waddup".encode(), (host_addr, port))
cur_addr = None
cur_name = None
path_end = False
cur_bytes = None
attempts_left = 3
timeReceived = 0
pingStartTime = 0
while not path_end and attempts_left > 0:
try:
pingStartTime = time.time()
cur_bytes, cur_addr = recv_socket.recvfrom(1024)
timeReceived = time.time()
path_end = True
cur_addr = cur_addr[0]
try:
cur_name = gethostbyaddr(cur_addr)[0]
except error:
cur_name = cur_addr
except error:
attempts_left -= 1
send_socket.close()
recv_socket.close()
if not path_end:
pass
if cur_addr is not None:
cur_host = "%s (%s) " % (cur_name, cur_addr)
else:
cur_host = ""
print("%d: %.0f ms " %
(
ttl,
(timeReceived - pingStartTime) * 1000,
) + cur_host
)
ttl += 1
if cur_addr == host_addr or ttl > max_hops:
break
I have set up the receiving socket for an ICMP packet as far as I can tell but it just hangs on recvfrom. I've allowed all ICMP connections on Windows Defender and when I run Wireshark, an appropriate ICMP packet is sent to my router.
The packet with the TTL expired message is the one I want to receive
I'm new to networking but all the code I've seen online has this exact setup. Would anyone be able to tell me why it's stalling and what I can do to fix it? I want the program to read the ICMP packet with the TTL expiration message.

Related

Python simple UDP server does not receive message with IP options

I have a simple udp server/client setup where I send a message from the client and print it on the server. This works well for a regular IP packet but the message is not received when I add an IP options header to the packet, even though I can sniff the packet using scapy.
Here's the packet without IP options
###[ Ethernet ]###
dst = 00:04:00:00:04:01
src = 00:aa:00:02:00:04
type = 0x800
###[ IP ]###
version = 4L
ihl = 5L
tos = 0x0
len = 47
id = 1
flags =
frag = 0L
ttl = 61
proto = udp
chksum = 0x62f4
src = 10.0.2.101
dst = 10.0.4.101
\options \
###[ UDP ]###
sport = 10001
dport = 3478
len = 27
chksum = 0x2bd1
###[ Raw ]###
load = 'message from a game'
And here's the packet with IP options header:
###[ Ethernet ]###
dst = 00:04:00:00:04:01
src = 00:aa:00:02:00:04
type = 0x800
###[ IP ]###
version = 4L
ihl = 8L
tos = 0x0
len = 59
id = 1
flags =
frag = 0L
ttl = 61
proto = udp
chksum = 0x5fe8
src = 10.0.2.101
dst = 10.0.4.101
\options \
|###[ IPOption ]###
| copy_flag = 1L
| optclass = control
| option = 31L
| length = 12
| value = '\x00\x01\x00\x00RTGAME'
###[ UDP ]###
sport = 10001
dport = 3478
len = 27
chksum = 0x2bd1
###[ Raw ]###
load = 'message from a game'
And here's the UDP server:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('', args.port))
while True:
try:
data, addr = sock.recvfrom(1024)
print("received: %s" % data)
except KeyboardInterrupt:
sock.close()
break
I've been stuck on this for a few days and would love if someone could figure it out.
Thanks
have just been playing and the following works as a self-contained/minimal working example for me with Python 3.7.1 under both OSX and Linux
generating a valid set of IP Options:
from scapy.all import IPOption, raw
ipopts = raw(IPOption(
copy_flag=1, optclass='control', option=31,
value='\x00\x01\x00\x00RTGAME'))
(if you don't have Scapy, the above should generate: b'\x9f\x0c\x00\x01\x00\x00RTGAME')
client code:
import socket
from time import sleep
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(('127.0.0.1', 3478))
s.setsockopt(socket.IPPROTO_IP, socket.IP_OPTIONS, ipopts)
while True:
s.send(b'message from a game')
sleep(1)
server code:
import socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.bind(('', 3478))
s.setsockopt(socket.IPPROTO_IP, socket.IP_RECVOPTS, 1)
while True:
print(*s.recvmsg(4096, 1024))
this should result in the "server" displaying lines like:
b'message from a game\n' [(0, 6, b'\x9f\x0c\x00\x01\x00\x00RTGAME')] 0 ('127.0.0.1', 46047)
furthermore, I can watch packets move over the network by running:
sudo tcpdump -i lo0 -vvv -n 'udp and port 3478'
at the command line, or this in Scapy:
sniff(iface='lo0', filter='udp and port 3478', prn=lambda x: x.show())
for some reason I don't actually receive the ancillary data containing the IP Options under OSX, but the data shows up in the packet sniffers.
The problem was due to an incorrect IPv4 checksum. I failed to mention in the question that I'm running this in a mininet environment with custom switches. The IP options get added in transit by a switch, but the checksum wasn't updated. Once I fixed that, the packet made it to the server.
Thanks for the help and pointers everyone!

Incorrect throughput value in python

I am writing a python code to find throughput between server and client. It is based on speedtest.net functionality where I am sending a dummy file to calculate the speed. The problem I am facing is unreliable throughput output. I will appreciate your suggestions on the same. Here is the code.
server.py
import socket
import os
port = 60000
s = socket.socket()
host = socket.gethostname()
s.bind((host, port))
s.listen(5)
print 'Server listening....'
while True:
conn, addr = s.accept()
print 'Got connection from', addr
data = conn.recv(1024)
print('Server received', repr(data))
filename='akki.txt'
b = os.path.getsize(filename)
f = open(filename,'rb')
l = f.read(b)
while (l):
conn.send(l)
l = f.read(b)
f.close()
print('Done sending')
conn.send('Thank you for connecting')
conn.close()
Client.py
import socket
import time
import os
s = socket.socket()
host = socket.gethostname()
port = 60000
t1 = time.time()
s.connect((host, port))
s.send("Hello server!")
with open('received_file', 'wb') as f:
print 'file opened'
t2 = time.time()
while True:
data = s.recv(1024)
if not data:
break
f.write(data)
t3 = time.time()
print data
print 'Total:', t3 - t1
print 'Throughput:', round((1024.0 * 0.001) / (t3 - t1), 3),
print 'K/sec.'
f.close()
print('Successfully received the file')
s.close()
print('connection closed')
Output when sending akki.txt
Server Output
Server listening....
Got connection from ('10.143.47.165', 60902)
('Server received', "'Hello server!'")
Done sending
Client output
file opened
Raw timers: 1503350568.11 1503350568.11 1503350568.11
Total: 0.00499987602234
**Throughput: 204.805 K/sec.**
Successfully received the file
connection closed
Output for ak.zip ( which is bigger file)
Client output
file opened
Total: 0.0499999523163
**Throughput: 20.48 K/sec.**
Successfully received the file
connection closed
Short Answer: you need to take the file size into consideration.
More Details:
Throughput is data/time. Your calculation:
round((1024.0 * 0.001) / (t3 - t1), 3)
Doesn't take the file size into account. Since sending a large file takes more time, 't3-t1' is bigger so your throughput is smaller (same numerator with larger denominator). Try adding the file size to the formula and you should get much more constant results.
Hope this helps.

Best way to handle multiple connections at the same time

I have an application which listens to multiple connections and verifies whether the user is active or not
I use a 1 thread socket handling method with WSAASyncSelect.
The problem is that sometimes when a lot of users connecting at the same time some users get no reply
i think it is because the "send" hasn't been called yet and the program has received another connection so it goes again to handle the new connection ignoring the previous one. Like WSAASyncSelect has triggered and now it processing a new connection instead of completing the previous request.
So what to do to fix this issue? i tried to stop the events from WSAASyncSelect temporary by calling it with zero parameters when handling the connection until finish it then re enable network events but that didn't help either.
Here are the codes that handling the events (recieve then decrypt and then compare the bytes then send data according to what in listbox ie Active user or not)
This called upon receive of FD_READ
WSAAsyncSelect s, frmMain.hwnd, 0, 0 'Disabling Notifications event
Do Until bytesRecieved = SOCKET_ERROR
bytesRecieved = recv(wParam, buffer(Bytes), 500, 0)
If bytesRecieved > 0 Then
Bytes = Bytes + bytesRecieved
ElseIf bytesRecieved = 0 Then
Exit Sub
End If
Loop
Call MemCopy(ByVal decryptedArrival, buffer(0), Bytes)
WSAAsyncSelect s, frmMain.hwnd, WINSOCKMSG, FD_CONNECT + FD_READ + FD_CLOSE + FD_ACCEPT + FD_WRITE
If frmMain.chkSaveLog.value = vbChecked Then
frmMain.txtConnectionsLog.Text = frmMain.txtConnectionsLog.Text & Now & " Receiving a connection (" & wParam & ")" & vbNewLine
AutoScroll
If frmMain.chkAutoSave.value = vbChecked Then
strCurrentLogLine = Now & " Receiving a connection (" & wParam & ")"
AutoSaveLog (strCurrentLogLine)
frmMain.cmdClearLogs.Enabled = True
End If
End If
Below here is a decryption of bytes then comparing by ID as byte identifier like 1 = check for update
2 - send user info etc
in a Select Case statement following by a send Api.
And the accepting procedure
This called upon receive of FD_ACCEPT
Function AcceptConnection(wParam As Long)
lpString = String(32, 0)
AcSock = accept(wParam, sockaddress, Len(sockaddress))
strTempIP = getascip(sockaddress.sin_addr)
frmMain.txtConnectionsLog.Text = frmMain.txtConnectionsLog.Text & Now & " Getting a connection from IP address: " & _
strTempIP & " (" & AcSock & ")" & vbNewLine
AutoScroll
If frmMain.chkAutoSave.value = vbChecked Then
strCurrentLogLine = Now & " Getting a connection from IP address: " & strTempIP & " (" & AcSock & ")" & vbNewLine
AutoSaveLog (strCurrentLogLine)
End If
End Function
Are there any suggestions for a better performance?
What you showed is NOT the correct way to use WSAAsyncSelect(). Try something more like this instead:
When creating a listening socket:
lSock = socket(...)
bind(lSock, ...)
listen(lSock, ...)
WSAAsyncSelect lSock, frmMain.hwnd, WINSOCKMSG, FD_ACCEPT
When a listening socket receives FD_ACCEPT:
Function AcceptConnection(wParam As Long)
AcSock = accept(wParam, sockaddress, Len(sockaddress))
If AcSock = INVALID_SOCKET Then
Exit Sub
End If
WSAAsyncSelect AcSock, frmMain.hwnd, WINSOCKMSG, FD_READ + FD_CLOSE + FD_WRITE
...
End Function
When an accepted client socket receives FD_READ:
Function ReadConnection(wParam As Long)
Do
bytesRecieved = recv(wParam, ReadBuffer(ReadBytes), 500, 0)
If bytesRecieved = SOCKET_ERROR Then
If WSAGetLastError() <> WSAEWOULDBLOCK Then
Exit Sub
End If
ElseIf bytesRecieved = 0 Then
Exit Sub
Else
ReadBytes = ReadBytes + bytesRecieved
End If
Loop Until bytesRecieved = SOCKET_ERROR
' process ReadBuffer up to ReadBytes number of bytes as needed...
' remove processed bytes from front of ReadBuffer and decrement ReadBytes accordingly
...
End Function
When an accepted client socket receives FD_WRITE:
Function WriteConnection(wParam As Long)
While SendBytes > 0
bytesSent = send(wParam, SendBuffer(0), SendBytes, 0)
If bytesSent = SOCKET_ERROR Then
Exit Sub
End If
' remove bytesSent number of bytes from front of SendBuffer ...
SendBytes = SendBytes - bytesSent;
End While
End Function
The trick is that you need to allocate separate ReadBuffer and SendBuffer buffers for each accepted client. Make sure that each time you receive FD_READ that you are appending bytes only to the ReadBuffer of the socket that triggered FD_READ, and each time you receive FD_WRITE that you are removing bytes only from the SendBuffer of the socket that triggered FD_WRITE.
When recv() has no more bytes to read, process that socket's ReadBuffer as needed, removing only complete messages from the front and leaving incomplete messages for later processing.
When send() fails with WSAEWOULDBLOCK, append any unsent bytes to the SendBuffer of the socket that caused send() to fail. When you receive an FD_WRITE event for a socket, check that socket's SenBuffer and resend any bytes that are in it, stopping when the buffer is exhausted or an WSAEWOULDBLOCK error occurs.
Very easy, and quite effective, way to do it is to fork out for every incoming connection. This will most likely require you to restructure your application, but the basic flow should be as follows:
1. New connection is opened to the server
2. Server accepts the connection and forks out
3. The fork closes the original socket for listening, so only the parent will be accepting new connections
4. And then your magic happens, separate from the original thread.
This way you do not have to worry about issues of concurrency, as long as your machine can handle all the traffic and load because each connections is independent.

Python 3: Server socket not closing properly, infinite loop of blank messages received

EDIT: I solved this question myself, look at the first answer if you have a similar issue
I am new to sockets and made a client-server test modifying one example, it sends and receives data ok but when closing the server get caught in a loop receiving blank messages and I can't find out why even though I use shutdown(socket.SHUT_RDWR) to close the connection immediately according to the documentation.
I had to use two cmd of windows for each one (server and client) because when running first the server in idle and then the client I got in client_example ConnectionRefusedError: [WinError 10061] No connection could be made because the target machine actively refused it here is the code. Anyways, here it is the sequence I tried:
(in the client console):
c:\python33\python client_example.py
SEND( TYPE q or Q to Quit):Hi
SEND( TYPE q or Q to Quit):q
(in the server console):
c:\python33\python server_example3.py
TCPServer Waiting for client on port 7000
I got a connection from ('127.0.0.1', 49263)
RECEIVED: b'Hi'
RECEIVED: b''
RECEIVED: b''
RECEIVED: b''
RECEIVED: b''
RECEIVED: b''
.... <---After I did CTRL+C
Traceback (most recent call last):
File "server_example.py", line 19, in <module>
print("RECEIVED:",data)
File "c:\python33\lib\encodings\cp850.py", line 19, in encode
return codecs.charmap_encode(input,self.errors,encoding_map)[0]
KeyboardInterrupt
Here is the code of the server and client:
server_example.py:
#TCP server example
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("", 7000))
server_socket.listen(5)
print("TCPServer Waiting for client on port 7000")
while 1:
client_socket, address = server_socket.accept()
print("I got a connection from ", address)
while 1:
data = client_socket.recv(32)
if (data == 'q' or data == 'Q'):
client_socket.shutdown(socket.SHUT_RDWR)
client_socket.close()
break;
else:
print("RECEIVED:",data)
client_example.py:
# TCP client example
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(("localhost", 7000))
while 1:
data = input( "SEND( TYPE q or Q to Quit):" )
if (data != 'Q' and data != 'q'):
my_bytes = bytearray()
for c in data:
my_bytes.append(ord(c))
client_socket.send(my_bytes)
else:
client_socket.shutdown(socket.SHUT_RDWR)
client_socket.close()
break;
Well, I feel dumb that I solved this thing after 11 minutes of posting the question when I was unable to solve it for 2 hours...
The problem was that I was comparing data with the string 'q' which was correct in python 2 but in python 3 what is being sent are bytes, b'q' <> 'q' ...
Here is the corrected code:
server_example.py
#TCP server example
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("", 7000))
server_socket.listen(5)
print("TCPServer Waiting for client on port 7000")
while 1:
client_socket, address = server_socket.accept()
print("I got a connection from ", address)
while 1:
data = client_socket.recv(32)
print("RECEIVED:",data)
if (data == b'q' or data == b'Q'):
## client_socket.shutdown(socket.SHUT_RDWR)
client_socket.close()
break;
break;
client_example.py
# TCP client example
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(("localhost", 7000))
while 1:
data = input( "SEND( TYPE q or Q to Quit):" )
if (data != 'Q' and data != 'q'):
my_bytes = bytearray()
for c in data:
my_bytes.append(ord(c))
client_socket.send(my_bytes)
else:
my_bytes = bytearray()
my_bytes.append(ord(data))
client_socket.send(my_bytes)
## client_socket.shutdown(socket.SHUT_RDWR)
client_socket.close()
break;
You're assuming you're going to get the Q. You should also engage in the same processing when you get EOS from read, whatever form that takes in Python.
#EJP : By EOS you mean end of stream? If so you are right, this example was a very simple one to get started in which I overlooked details like these.
Anyway I found that using this check in the server it is solved:
data = input( "SEND( TYPE q or Q to Quit):" )
if (data == b''):
break;

packetsocket opened on loopback device receives all the packets twice. How to filter these duplicate entries?

when i open a packetsocket on a loopback interface (lo) and listen all the packets are seen twice. why is it so?
But a capture on the interface using tcpdump correctly ignores the duplicate entries. see the 'packets received by filter' (which contains the duplicate packets) and 'packets captured'. How is this filtering done
tcpdump -i lo -s 0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
11:00:08.439542 IP 12.0.0.3 > localhost.localdomain: icmp 64: echo request seq 1
11:00:08.439559 IP localhost.localdomain > 12.0.0.3: icmp 64: echo reply seq 1
11:00:09.439866 IP 12.0.0.3 > localhost.localdomain: icmp 64: echo request seq 2
11:00:09.439884 IP localhost.localdomain > 12.0.0.3: icmp 64: echo reply seq 2
11:00:10.439389 IP 12.0.0.3 > localhost.localdomain: icmp 64: echo request seq 3
11:00:10.439410 IP localhost.localdomain > 12.0.0.3: icmp 64: echo reply seq 3
6 packets captured
12 packets received by filter
0 packets dropped by kernel
My code:
int main()
{
int sockFd;
if ( (sockFd=socket(PF_PACKET, SOCK_DGRAM, 0))<0 ) {
perror("socket()");
return -1;
}
/* bind the packet socket */
struct sockaddr_ll addr;
struct ifreq ifr;
strncpy (ifr.ifr_name, "lo", sizeof(ifr.ifr_name));
if(ioctl(sockFd, SIOCGIFINDEX, &ifr) == -1)
{
perror("iotcl");
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sll_family=AF_PACKET;
addr.sll_protocol=htons(ETH_P_ALL);
addr.sll_ifindex=ifr.ifr_ifindex;
if ( bind(sockFd, (struct sockaddr *)&addr, sizeof(addr)) ) {
perror("bind()");
return -1;
}
char buffer[MAX_BUFFER+1];
int tmpVal = 1;
while(tmpVal > 0)
{
tmpVal = recv (sockFd, buffer, MAX_BUFFER, 0);
cout<<"Received Pkt with Bytes "<<tmpVal <<endl;
}
}
Figured out the problem.
from libcaps code:
* - The loopback device gives every packet twice; on 2.2[.x] kernels,
* if we use PF_PACKET, we can filter out the transmitted version
* of the packet by using data in the "sockaddr_ll" returned by
* "recvfrom()", but, on 2.0[.x] kernels, we have to use
* PF_INET/SOCK_PACKET, which means "recvfrom()" supplies a
* "sockaddr_pkt" which doesn't give us enough information to let
* us do that.
the listening entity needs to filter the duplicate packet using the if_index got from recvfrom api.