Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of RawSocket using ljsyscall #655

Merged
merged 7 commits into from
Jan 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 0 additions & 38 deletions src/apps/socket/dev.lua

This file was deleted.

58 changes: 0 additions & 58 deletions src/apps/socket/io.c

This file was deleted.

5 changes: 0 additions & 5 deletions src/apps/socket/io.h

This file was deleted.

106 changes: 78 additions & 28 deletions src/apps/socket/raw.lua
Original file line number Diff line number Diff line change
@@ -1,72 +1,122 @@
module(...,package.seeall)
module(..., package.seeall)

local app = require("core.app")
local link = require("core.link")
local S = require("syscall")
local h = require("syscall.helpers")
local bit = require("bit")
local link = require("core.link")
local packet = require("core.packet")
local dev = require("apps.socket.dev").dev
local ffi = require("ffi")
local C = ffi.C

local c, t = S.c, S.types.t

RawSocket = {}

function RawSocket:new (ifname)
assert(ifname)
self.__index = self
return setmetatable({dev = dev:new(ifname)}, self)
local tp = h.htons(c.ETH_P["ALL"])
local sock, err = S.socket(c.AF.PACKET, bit.bor(c.SOCK.RAW, c.SOCK.NONBLOCK), tp)
if not sock then error(err) end
local index, err = S.util.if_nametoindex(ifname, sock)
if err then
S.close(sock)
error(err)
end
local addr = t.sockaddr_ll{sll_family = c.AF.PACKET, sll_ifindex = index, sll_protocol = tp}
local ok, err = S.bind(sock, addr)
if not ok then
S.close(sock)
error(err)
end
return setmetatable({sock = sock}, {__index = RawSocket})
end

function RawSocket:pull ()
local l = self.output.tx
if l == nil then return end
while not link.full(l) and self.dev:can_receive() do
link.transmit(l, self.dev:receive())
while not link.full(l) and self:can_receive() do
link.transmit(l, self:receive())
end
end

function RawSocket:can_receive ()
local ok, err = S.select({readfds = {self.sock}}, 0)
return not (err or ok.count == 0)
end

function RawSocket:receive ()
local buffer = ffi.new("uint8_t[?]", C.PACKET_PAYLOAD_SIZE)
local sz, err = S.read(self.sock, buffer, C.PACKET_PAYLOAD_SIZE)
if not sz then return err end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that a packet.free(p) is needed here to avoid a resource leak.

return packet.from_pointer(buffer, sz)
end

function RawSocket:push ()
local l = self.input.rx
if l == nil then return end
while not link.empty(l) and self.dev:can_transmit() do
while not link.empty(l) and self:can_transmit() do
local p = link.receive(l)
self.dev:transmit(p)
self:transmit(p)
packet.free(p)
end
end

function RawSocket:can_transmit ()
local ok, err = S.select({writefds = {self.sock}}, 0)
return not (err or ok.count == 0)
end

function RawSocket:transmit (p)
local sz, err = S.write(self.sock, packet.data(p), packet.length(p))
if not sz then return err end
return sz
end

function RawSocket:stop()
S.close(self.sock)
end

function selftest ()
-- Send a packet over the loopback device and check
-- that it is received correctly.
-- XXX beware of a race condition with unrelated traffic over the
-- loopback device
-- XXX Beware of a race condition with unrelated traffic over the
-- loopback device.
local datagram = require("lib.protocol.datagram")
local ethernet = require("lib.protocol.ethernet")
local ipv6 = require("lib.protocol.ipv6")

-- Initialize RawSocket.
local lo = RawSocket:new("lo")
lo.input, lo.output = {}, {}
lo.input.rx, lo.output.tx = link.new("test1"), link.new("test2")
-- Construct packet.
local dg_tx = datagram:new()
local src = ethernet:pton("02:00:00:00:00:01")
local dst = ethernet:pton("02:00:00:00:00:02")
local localhost = ipv6:pton("0:0:0:0:0:0:0:1")
dg_tx:push(ipv6:new({src = localhost,
dst = localhost,
next_header = 59, -- no next header
next_header = 59, -- No next header.
hop_limit = 1}))
dg_tx:push(ethernet:new({src = src, dst = dst, type = 0x86dd}))

local link = require("core.link")
local lo = RawSocket:new("lo")
lo.input, lo.output = {}, {}
lo.input.rx, lo.output.tx = link.new("test1"), link.new("test2")
dg_tx:push(ethernet:new({src = src,
dst = dst,
type = 0x86dd}))
-- Transmit packet.
link.transmit(lo.input.rx, dg_tx:packet())
lo:push()
-- Receive packet.
lo:pull()
local dg_rx = datagram:new(link.receive(lo.output.tx), ethernet)
assert(dg_rx:parse({ { ethernet, function(eth)
return(eth:src_eq(src) and eth:dst_eq(dst)
and eth:type() == 0x86dd)
end },
{ ipv6, function(ipv6)
return(ipv6:src_eq(localhost) and
ipv6:dst_eq(localhost))
end } }), "loopback test failed")
-- Assert packet was received OK.
assert(dg_rx:parse({{ethernet, function(eth)
return(eth:src_eq(src) and eth:dst_eq(dst) and eth:type() == 0x86dd)
end }, { ipv6, function(ipv6)
return(ipv6:src_eq(localhost) and ipv6:dst_eq(localhost))
end } }), "loopback test failed")
lo:stop()
print("selftest passed")

-- Another useful test would be to feed a pcap file with
-- XXX Another useful test would be to feed a pcap file with
-- pings to 127.0.0.1 and ::1 into lo and capture/compare
-- the responses with a pre-recorded pcap.
end
39 changes: 0 additions & 39 deletions src/lib/raw/raw.c

This file was deleted.

1 change: 0 additions & 1 deletion src/lib/raw/raw.h

This file was deleted.