Skip to content

Instantly share code, notes, and snippets.

@creationix
Last active September 23, 2015 20:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save creationix/b7f949c3e360773bece2 to your computer and use it in GitHub Desktop.
Save creationix/b7f949c3e360773bece2 to your computer and use it in GitHub Desktop.
local ffi = require 'ffi'
local uv = require 'uv'
local os = require('os')
local AF_INET = 2
local AF_INET6 = jit.os == "OSX" and 30 or
jit.os == "Linux" and 10 or error("Unknown OS")
local SOCK_RAW = 3
local IPPROTO_ICMP = 1
local IPPROTO_ICMP6 = 58
ffi.cdef[[
int socket(int socket_family, int socket_type, int protocol);
]]
local band = bit.band
local bor = bit.bor
local bnot = bit.bnot
local lshift = bit.lshift
local rshift = bit.rshift
local byte = string.byte
local char = string.char
local time = os.time
-- Calculate 16-bit one's complement of the one's complement sum
local function checksum(buffer)
local sum = 0
for i = 1, #buffer, 2 do
local word = bor(lshift(byte(buffer, i), 8), byte(buffer, i + 1))
sum = sum + word
if sum > 0xffff then
sum = sum - 0xffff -- remove carry bit and add 1
end
end
-- Take complement
sum = band(bnot(sum), 0xffff)
-- Return as 2-byte string in network byte order
return char(rshift(sum, 8), band(sum, 0xff))
end
local function sock4(ip)
local sockfd = ffi.C.socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
assert(sockfd >= 0, "Failed to create socket")
local raw = uv.new_udp()
assert(raw:open(sockfd))
return raw
end
local function sock6(ip)
local sockfd = ffi.C.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMP6)
assert(sockfd >= 0, "Failed to create socket")
local raw = uv.new_udp()
assert(raw:open(sockfd))
return raw
end
local id = math.random(0x10000) % 0x10000
local seq = 0
local function ping(ip, payload)
local now = time()
local top, sock
if ip:match("^%d+%.%d+%.%d+%.%d+$") then
top = "\x08\x00"
sock = sock4(ip)
else
top = "\x80\x00"
sock = sock6(ip)
end
local bottom = char(
band(rshift(id, 8), 0xff), band(id, 0xff), -- id
band(rshift(seq, 8), 0xff), band(seq, 0xff), -- seq
-- timestamp
band(rshift(now, 24), 0xff), band(rshift(now, 16), 0xff),
band(rshift(now, 8), 0xff), band(now, 0xff)
) .. payload
seq = (seq + 1) % 256
sock:send(top .. checksum(top .. "\x00\x00" .. bottom) .. bottom, ip, 0)
sock:recv_start(function (err, message, address)
assert(not err, err)
p { message = message, address = address }
sock:recv_stop()
end)
end
local e4payload = "\x00\x02\x7b\x14\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" ..
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" ..
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" ..
"\x30\x31\x32\x33\x34\x35\x36\x37"
local e6payload = "\x56\x03\x01\xc9\x00\x01\xa3\x7d"
uv.new_timer():start(0, 1000, function ()
ping("127.0.0.1", e4payload)
ping("::1", e6payload)
ping("23.253.227.83", e4payload)
ping("2001:4800:7817:103:be76:4eff:fe05:3fa8", e6payload)
end)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment