I have threaded Python 2.7.12 code that waits for connections from external devices, and then spins off a thread to work with each device. 80% of the time this works fine, but every once in a while I get unwanted RST from the OS or Python, I'm not sure which.
The TCP connection starts off normally and then abrubptly is reset.
SYN, SYN/ACK, ACK, RST
The server initiates the RST. The timing is also extremely tight so I feel like this is probably Python's socket bugging out or the OS taking over for some reason. How could I debug this? Since I'm not seeing any errors in my Python code and logging itself, is there a ways to debug the sockets code directly used by Python?
Here is the code:
while True:
host = socket.gethostbyname(socket.gethostname())
port = 3001
try:
tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpsock.bind((host,port))
tcpsock.listen(5)
print ("\nListening for incoming connections...")
(clientsock, (ip, port)) = tcpsock.accept()
newthread = ClientThread(ip, port, clientsock)
newthread.start()
threads = 0
for t in threading.enumerate():
threads += 1
logger.info('######################## THREADS = %s' % (threads))
except Exception as e:
logger.critical('Exception: %s. Error initializing thread' % (e))
EDIT1 - Adding ClientThread code:
class ClientThread(threading.Thread):
def __init__(self,ip,port,clientsocket):
threading.Thread.__init__(self)
self.ip = ip
self.port = port
self.csocket = clientsocket
The rest of the code just starts working with the device.
The RST packet means "I don't have a connection open for the packet you just sent me". You're right, if you follow the simple examples on python.org, then Python will generate RST packets. For example:
#!/usr/bin/python2.7
import socket, sys, time
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind the socket to the port
server_port = 10000
print 'starting up on port %s' % server_port
sock.bind(('', server_port))
# Listen for incoming connections
sock.listen(1)
print 'waiting for a connection'
conn, cli_addr = sock.accept()
print 'connection from', cli_addr
data = "Hello, World!\n" \
+ "Your IP is " + cli_addr[0] + "\n"
HTTP = "HTTP/1.1 200 OK\nContent-Type: text/html\nContent-Length:"
conn.sendall(HTTP + str(len(data)) + "\n\n" + data);
conn.close()
tcpdump then says:
10:59:48.847443 IP 127.0.0.1.38330 > 127.0.0.1.10000: Flags [S], seq 1326297161, win 43690, options [mss 65495,sackOK,TS val 3126487416 ecr 0,nop,wscale 7], length 0
10:59:48.847462 IP 127.0.0.1.10000 > 127.0.0.1.38330: Flags [S.], seq 1677934801, ack 1326297162, win 43690, options [mss 65495,sackOK,TS val 3126487416 ecr 3126487416,nop,wscale 7], length 0
10:59:48.847475 IP 127.0.0.1.38330 > 127.0.0.1.10000: Flags [.], ack 1, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 0
10:59:48.847547 IP 127.0.0.1.38330 > 127.0.0.1.10000: Flags [P.], seq 1:80, ack 1, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 79
10:59:48.847557 IP 127.0.0.1.10000 > 127.0.0.1.38330: Flags [.], ack 80, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 0
10:59:48.847767 IP 127.0.0.1.10000 > 127.0.0.1.38330: Flags [P.], seq 1:95, ack 80, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 94
10:59:48.847790 IP 127.0.0.1.38330 > 127.0.0.1.10000: Flags [.], ack 95, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 0
10:59:48.847861 IP 127.0.0.1.10000 > 127.0.0.1.38330: Flags [R.], seq 95, ack 80, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 0
10:59:48.847862 IP 127.0.0.1.38330 > 127.0.0.1.10000: Flags [F.], seq 80, ack 95, win 342, options [nop,nop,TS val 3126487416 ecr 3126487416], length 0
10:59:48.847878 IP 127.0.0.1.10000 > 127.0.0.1.38330: Flags [R], seq 1677934896, win 0, length 0
The problem is that conn.close() destroys the socket data structure, so the OS doesn't recognize the next packet. Notice that the client (curl) is sending a nice FIN packet, but the server (Python code above) is not.
To solve this, you need to send a FIN packet by calling conn.shutdown(socket.SHUT_WR) and then leave the socket structure around long enough to catch any straggling packets (you could do this in another thread). So instead of conn.close(), do:
conn.shutdown(socket.SHUT_WR)
time.sleep(1)
conn.close()
With this change, both sides send FIN, and the RST are missing from tcpdump:
11:05:50.201352 IP 127.0.0.1.38338 > 127.0.0.1.10000: Flags [S], seq 4249130338, win 43690, options [mss 65495,sackOK,TS val 3126848770 ecr 0,nop,wscale 7], length 0
11:05:50.201368 IP 127.0.0.1.10000 > 127.0.0.1.38338: Flags [S.], seq 1410528158, ack 4249130339, win 43690, options [mss 65495,sackOK,TS val 3126848770 ecr 3126848770,nop,wscale 7], length 0
11:05:50.201381 IP 127.0.0.1.38338 > 127.0.0.1.10000: Flags [.], ack 1, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
11:05:50.201441 IP 127.0.0.1.38338 > 127.0.0.1.10000: Flags [P.], seq 1:80, ack 1, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 79
11:05:50.201450 IP 127.0.0.1.10000 > 127.0.0.1.38338: Flags [.], ack 80, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
11:05:50.201568 IP 127.0.0.1.10000 > 127.0.0.1.38338: Flags [P.], seq 1:95, ack 80, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 94
11:05:50.201594 IP 127.0.0.1.38338 > 127.0.0.1.10000: Flags [.], ack 95, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
11:05:50.201643 IP 127.0.0.1.10000 > 127.0.0.1.38338: Flags [F.], seq 95, ack 80, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
11:05:50.201691 IP 127.0.0.1.38338 > 127.0.0.1.10000: Flags [F.], seq 80, ack 96, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
11:05:50.201707 IP 127.0.0.1.10000 > 127.0.0.1.38338: Flags [.], ack 81, win 342, options [nop,nop,TS val 3126848770 ecr 3126848770], length 0
Related
Currently trying to make handshake process on python using raw sockets but for some reason I can't send any packet with TCP protocol receiving OSError: [WinError 10022] An invalid argument was supplied. Here is my code:
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
s.sendto(packet, ('8.8.8.8', 80))
As packet I have tried to use scapy's TCP packet, TCP bytes from wireshark after successful sendings with other libraries and also hand-made byte strings:
def chksum(packet: bytes) -> int:
if len(packet) % 2 != 0:
packet += b'\0'
res = sum(array.array("H", packet))
res = (res >> 16) + (res & 0xffff)
res += res >> 16
return (~res) & 0xffff
class TCPPacket:
def __init__(self,
src_host: str,
src_port: int,
dst_host: str,
dst_port: int,
flags: int = 0):
self.src_host = src_host
self.src_port = src_port
self.dst_host = dst_host
self.dst_port = dst_port
self.flags = flags
def build(self) -> bytes:
packet = struct.pack(
'!HHIIBBHHH',
self.src_port, # Source Port
self.dst_port, # Destination Port
0, # Sequence Number
0, # Acknoledgement Number
5 << 4, # Data Offset
self.flags, # Flags
8192, # Window
0, # Checksum (initial value)
0 # Urgent pointer
)
pseudo_hdr = struct.pack(
'!4s4sHH',
socket.inet_aton(self.src_host), # Source Address
socket.inet_aton(self.dst_host), # Destination Address
socket.IPPROTO_TCP, # PTCL
len(packet) # TCP Length
)
checksum = chksum(pseudo_hdr + packet)
packet = packet[:16] + struct.pack('H', checksum) + packet[18:]
return packet
So literally no idea why my socket doesn't like any packet
I found out what is wrong. Windows doesn't allow to send TCP packets with raw sockets so this code will never work. Probably it is possible to write the same with scapy or using other libraries but that's not what I need so the only way to make it work is to run on linux. Still not sure if the packet creation is correct but TCP protocol with raw sockets sure works fine on linux.
I'm trying to code a packet sniffer that detects the different websites that are visited by the devices connected to my wifi. The problem is that I'm only getting private IPs, here are the counted IPs I get from a 1 hour sniffing :
IPS Number_Of_Values
224.0.0.251 1068
192.168.1.255 387
255.255.255.255 32
224.0.0.22 28
224.0.0.1 28
192.168.1.111 6
192.168.1.115 5
224.0.1.187 2
Here is a part of the sniffer, ethernet_frame is getting mac addresses and proto, ipv4_packet unpacks the IP header and gets the IPs (source and target) and finally get_packets proceeds to the different unpackings:
def ethernet_frame(data):
dest_mac, src_mac, proto = struct.unpack('! 6s 6s H', data[:14])
return get_mac_addr(dest_mac), get_mac_addr(src_mac), socket.htons(proto), data[14:]
def ipv4_packet(data):
version_header_length = data[0]
version = version_header_length >> 4
header_length = (version_header_length & 15) * 4
ttl, proto, src, target = struct.unpack('! 8x B B 2x 4s 4s', data[:20])
return version, header_length, ttl, proto, ipv4(src), ipv4(target), data[header_length:]
def get__packets():
conn = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(3))
while True:
raw_data, addr = conn.recvfrom(65536)
dest_mac, src_mac, eth_proto, data = ethernet_frame(raw_data)
# 8 for ipv4
if eth_proto == 8:
(version, header_length, ttl, proto, src_ip, target_ip, data) = ipv4_packet(data)
Why am I only getting private IPs? How can I get websites IPs? If it's not possible, how to get the http header from a TCP packet in order to get the url?
(I'm coding it on my raspberry pi 4 - Linux)
I would like to send IGMP packets using scapy, specifically IGMP Leave, IGMP Membership report. Is it possible to do so?
UPDATE:
I was able to eventually generate them. Had to do the following:
1) Install scapy v.2.2.0 as it's described here (including minor alteration in setup.py):
scapy's contrib is missing after installing scapy on both windows and fedora
2) You need to use file from contribution package (features not added to the core of scapy):
import scapy.contrib.igmp
igmpPacket = scapy.contrib.igmp.IGMP()
Yes, it is possible to send IGMP packets. After googling a bit, I came up with some useful links that can help you in some direction.
On github there exists a IGMP and IGMPv3 implementation in scapy. Here is an interesting mailing list too. Also, this post has an other interesting stuff related to IGMP.
With this method, you can send out IGMP version 2 (RFC2236) Membership Query message, not IGMP version 3.
Here are complete code and tcpdump:
>>> from scapy.all import *
>>> import scapy.contrib.igmp
>>> p = IP(dst="62.22.14.4")/scapy.contrib.igmp.IGMP()
>>> send(p)
.
Sent 1 packets.
>>>
# tcpdump -ni cplane0 igmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on cplane0, link-type EN10MB (Ethernet), capture size 262144 bytes
18:42:01.045618 IP 44.60.11.3 > 62.22.14.4: igmp query v2 [max resp time 20]
18:42:01.045631 IP 44.60.11.3 > 62.22.14.4: igmp query v2 [max resp time 20]
18:42:01.046470 IP 44.60.11.3 > 62.22.14.4: igmp query v2 [max resp time 20]
18:42:01.046476 IP 44.60.11.3 > 62.22.14.4: igmp query v2 [max resp time 20]
18:42:01.959331 IP 62.22.14.4 > 224.1.1.1: igmp v2 report 224.1.1.1
Update:
Since IGMPv3 is under construction. Here is a way to send IGMP version 3 Membership Query:
>>> from scapy.all import *
>>>
>>> class IGMP3(Packet):
... name = "IGMP3"
... fields_desc = [ ByteField("type", 0x11),
... ByteField("mrtime", 20),
... XShortField("chksum", None),
... IPField("gaddr", "0.0.0.0"),
... IntField("others", 0x0)]
... def post_build(self, p, pay):
... p += pay
... if self.chksum is None:
... ck = checksum(p)
... p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:]
... return p
...
>>> bind_layers( IP, IGMP3, frag=0, proto=2)
>>> p = IP(dst="62.21.20.21")/IGMP3()
>>> send(p)
.
Sent 1 packets.
>>>
# tcpdump -ni cplane0 igmp -v
tcpdump: listening on cplane0, link-type EN10MB (Ethernet), capture size 262144 bytes
17:24:35.013987 IP (tos 0x0, ttl 62, id 1, offset 0, flags [none], proto IGMP (2), length 32)
44.60.11.3 > 62.21.20.21: igmp query v3 [max resp time 2.0s]
17:24:35.014000 IP (tos 0x0, ttl 62, id 1, offset 0, flags [none], proto IGMP (2), length 32)
44.60.11.3 > 62.21.20.21: igmp query v3 [max resp time 2.0s]
17:24:35.014476 IP (tos 0x0, ttl 62, id 1, offset 0, flags [none], proto IGMP (2), length 32)
44.60.11.3 > 62.21.20.21: igmp query v3 [max resp time 2.0s]
17:24:35.014482 IP (tos 0x0, ttl 62, id 1, offset 0, flags [none], proto IGMP (2), length 32)
44.60.11.3 > 62.21.20.21: igmp query v3 [max resp time 2.0s]
17:24:35.218208 IP (tos 0xc0, ttl 1, id 0, offset 0, flags [DF], proto IGMP (2), length 40, options (RA))
62.21.20.21 > 224.0.0.22: igmp v3 report, 1 group record(s) [gaddr 239.1.1.1 is_ex, 0 source(s)]
Similar to Setting TCP receive window in C and working with tcpdump in Linux and Why changing value of SO_RCVBUF doesn't work?, I'm a unable to increase the initial tcp receive window greater than 5888 on ubuntu linux 2.6.32-45
#!/usr/bin/python
from socket import socket, SOL_SOCKET, SO_RCVBUF, TCP_WINDOW_CLAMP
sock = socket()
sock.setsockopt(SOL_SOCKET, SO_RCVBUF, 65536)
sock.setsockopt(SOL_SOCKET, TCP_WINDOW_CLAMP, 32768)
sock.connect(('google.com', 80))
tcpdump says:
me > google: Flags [S], seq 3758517838, win 5840, options [mss 1460,sackOK,TS val 879735044 ecr 0,nop,wscale 6], length 0
google > me: Flags [S.], seq 597037042, ack 3758517839, win 62392, options [mss 1430,sackOK,TS val 541301157 ecr 879735044,nop,wscale 6], length 0
me > google: Flags [.], ack 1, win 92, options [nop,nop,TS val 879735051 ecr 541301157], length 0
sysctl -a | grep net.*mem says:
net.core.wmem_max = 131071
net.core.rmem_max = 131071
net.core.wmem_default = 112640
net.core.rmem_default = 112640
net.core.optmem_max = 10240
net.ipv4.igmp_max_memberships = 20
net.ipv4.tcp_mem = 77376 103168 154752
net.ipv4.tcp_wmem = 4096 16384 3301376
net.ipv4.tcp_rmem = 4096 87380 3301376
net.ipv4.udp_mem = 77376 103168 154752
net.ipv4.udp_rmem_min = 4096
net.ipv4.udp_wmem_min = 4096
Could there be something else putting a receive window limit on my connection?
That looks like the effects of TCP slow-start. This kernel thread from 2008 offers up a good explanation (I've linked to the last response in the thread):
SO_RCVBUF doesn't change receiver advertised window
If you keep watching the stream, the window size should increase, up to the maximum you set.
I'm just trying out zeromq for a project in python. I'm trying to bind the 'client' side and connect the server to the fixed location. I have a simple REQ/REP setup that works fine locally but seems to do nothing over a network. If I reverse the binding, then this also works over the network.
The relevant code is:
def respond(sock):
message = sock.recv()
response = "world"
sock.send(response)
print("Received '{0:s}', sent '{1:s}'.".format(message, response) )
def request(sock):
message = "Hello"
sock.send(message)
response = sock.recv()
print("Sent '{0:s}', recieved '{1:s}'".format(message, response) )
def main():
opts = get_opts()
if opts.client:
sock = CONTEXT.socket(zmq.REQ)
sock.bind("tcp://*:{0:d}".format(opts.port) )
request(sock)
if opts.server:
sock = CONTEXT.socket(zmq.REP)
sock.connect("tcp://{0:s}:{1:d}".format(opts.address, opts.port) )
while True:
respond(sock)
And a (non-)working example is here: https://gist.github.com/4071783
When connecting to a remote address, nothing seems to happen. If I check with tcpdump, I can certainly see activity on the port:
12:20:18.846927 IP server.58387 > client.5555: Flags [.], ack 1, win 3650, options [nop,nop,TS val 718051 ecr 46170252], length 0
12:20:18.847156 IP client.5555 > server.58387: Flags [P.], seq 1:3, ack 1, win 227, options [nop,nop,TS val 46170252 ecr 718051], length 2
12:20:18.847349 IP server.58387 > client.5555: Flags [P.], seq 1:3, ack 1, win 3650, options [nop,nop,TS val 718051 ecr 46170252], length 2
12:20:18.847373 IP client.5555 > server.58387: Flags [.], ack 3, win 227, options [nop,nop,TS val 46170252 ecr 718051], length 0
12:20:18.847553 IP client.5555 > server.58387: Flags [P.], seq 3:16, ack 3, win 227, options [nop,nop,TS val 46170252 ecr 718051], length 13
12:20:18.847645 IP server.58387 > client.5555: Flags [.], ack 3, win 3650, options [nop,nop,TS val 718051 ecr 46170252], length 0
12:20:18.848286 IP server.58387 > client.5555: Flags [.], ack 16, win 3650, options [nop,nop,TS val 718051 ecr 46170252], length 0
But the send() and recv() are still blocked as if waiting for a connection. Does anyone know what this is or could suggest how to debug it?
Can you ping the remote address? 0MQ is just using TCP at this level so if the connect fails, it's because the address you're trying to connect to is not reachable.