<commit_before>module(..., package.seeall)

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

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

RawSocket = {}

function RawSocket:new (ifname)
   assert(ifname)
   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: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
   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:can_transmit() do
      local p = link.receive(l)
      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.
   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.
                        hop_limit = 1}))
   dg_tx:push(ethernet:new({src = src, 
                            dst = dst, 
                            type = 0x86dd}))
   -- Transmit packet.
   link.transmit(lo.input.rx, dg_tx:packet())
   lo:push()<commit_msg>error on transmit failures; use sock:close() instead of S.close() to prevent double-frees<commit_after>module(..., package.seeall)

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

--ljsyscall returns error as a a cdata instead of a string,
--and the standard assert() doesn't use tostring() on it.
local function assert(v, ...)
   if not v then error(tostring(... or 'assertion failed'), 2) end
   return v, ...
end

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

RawSocket = {}

function RawSocket:new (ifname)
   assert(ifname)
   local tp = h.htons(c.ETH_P["ALL"])
   local sock = assert(S.socket(c.AF.PACKET, bit.bor(c.SOCK.RAW, c.SOCK.NONBLOCK), tp))
   local index, err = S.util.if_nametoindex(ifname)
   if not index then
      sock:close()
      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
      sock:close()
      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:can_receive() do
      link.transmit(l, self:receive())
   end
end

function RawSocket:can_receive ()
   local t, err = S.select({readfds = {self.sock}}, 0)
   while not t and (err.AGAIN or err.INTR) do
      t, err = S.select({readfds = {self.sock}}, 0)
   end
   assert(t, err)
   return t.count == 1
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)
   assert(sz, err)
   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:can_transmit() do
      local p = link.receive(l)
      self:transmit(p)
      packet.free(p)
   end
end

function RawSocket:can_transmit ()
   local t, err = S.select({writefds = {self.sock}}, 0)
   while not t and (err.AGAIN or err.INTR) do
      t, err = S.select({writefds = {self.sock}}, 0)
   end
   assert(t, err)
   return t.count == 1
end

function RawSocket:transmit (p)
   local sz, err = S.write(self.sock, p.data, p.length)
   assert(sz, err)
   assert(sz == p.length)
end

function RawSocket:stop()
   self.sock:close()
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.
   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.
                        hop_limit = 1}))
   dg_tx:push(ethernet:new({src = src,
                            dst = dst,
                            type = 0x86dd}))
   -- Transmit packet.
   link.transmit(lo.input.rx, dg_tx:packet())
   lo:push()
{'fraction_non_alphanumeric': 0.13663307646710135, 'fraction_numerical': 0.014967397747480736, 'mean_word_length': 2.9147331786542923, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3359627', 'n_tokens_mistral': 2431, 'n_tokens_neox': 2343, 'n_words': 639}