I have a raw Python socket initialized like so:
mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
mySocket.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
My problem is that I can't send any packet from the socket where the protocol field in my IP header is specified as '1' (ICMP). If this field is anything else it'll work just fine, including '17' (UDP) for example. I don't get any errors, the packet just doesn't display in Wireshark, and I can't receive it on my server.
I'm generating the IP header like so:
def getIPHeader(src, dst, proto):
version = 4
IHL = 5
DSCP = 0
ECN = 0
totalLength = 40
identification = 333
flags = 0
fragmentOffset = 0
timeToLive = 128
protocol = proto
headerChecksum = 0
sourceIP = socket.inet_aton(src)
destIP = socket.inet_aton(dst)
options = 0
version_IHL = (version << 4) | IHL
DSCP_ECN = (DSCP << 2) | ECN
flags_fragmentOffset = (flags << 13) | fragmentOffset
# The '!' ensures all arguments are converted to network byte order
header = struct.pack("!BBHHHBBH4s4s", version_IHL, DSCP_ECN, totalLength, identification, flags_fragmentOffset, timeToLive, protocol, headerChecksum, sourceIP, destIP)
return header
And sending like so:
icmpHeader = struct.pack("!bbHHh", 8, 0, 0, 666, 0)
packet = getIPHeader("192.168.2.15", "8.8.8.8", 1) + icmpHeader
mySocket.sendto(packet, ("8.8.8.8", 0))
Sending with UDP protocol set works (obviously malformed):
icmpHeader = struct.pack("!bbHHh", 8, 0, 0, 666, 0)
packet = getIPHeader("192.168.2.15", "8.8.8.8", 17) + icmpHeader
mySocket.sendto(packet, ("8.8.8.8", 0))
The worst part is if I send a ping from the command line, then copy the exact hex from Wireshark of the IP and ICMP data into my code, it STILL doesn't work, like so:
packet = b'\x45\x00\x00\x3c\x24\xad\x00\x00\x80\x01\x43\x4d\xc0\xa8\x02\x0f\x08\x08\x08\x08\x08\x00\x4d\x44\x00\x01\x00\x17\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x61\x62\x63\x64\x65\x66\x67\x68\x69'
mySocket.sendto(packet, ("8.8.8.8", 0))
However, as before, if I change the 10th byte of the hard coded data to '\x11' (17 decimal, UDP), I can then see it in Wireshark (obviously with a malformed UDP section as the rest of the packet is ICMP).
I'm running Windows 10, Python 3.4.1, and my firewall is off. Any ideas?
UPDATE: I've tried this on two different computers, a Windows 8 and a Windows 10 machine, both worked, so it's something to do with my computer, the plot thickens...