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

-- This module implements a level 7 firewall app that consumes the result
-- of DPI scanning done by l7spy.
--
-- The firewall rules are a table mapping protocol names to either
--   * a simple action ("drop", "reject", "accept")
--   * a pfmatch expression

local ffi      = require("ffi")
local link     = require("core.link")
local packet   = require("core.packet")
local datagram = require("lib.protocol.datagram")
local ether    = require("lib.protocol.ethernet")
local icmp     = require("lib.protocol.icmp.header")
local ipv4     = require("lib.protocol.ipv4")
local ipv6     = require("lib.protocol.ipv6")
local tcp      = require("lib.protocol.tcp")
local match    = require("pf.match")

ffi.cdef[[
  void syslog(int priority, const char*format, ...);
]]

L7Fw = {}
L7Fw.__index = L7Fw

-- create a new firewall app object given an instance of Scanner
-- and firewall rules
function L7Fw:new(config)
   local obj = { local_ipv4 = config.local_ipv4,
                 local_ipv6 = config.local_ipv6,
                 local_macaddr = config.local_macaddr,
                 scanner = config.scanner,
                 rules = config.rules,
                 -- this map tracks flows to compiled pfmatch functions
                 -- so that we only compile them once per flow
                 handler_map = {},
                 -- log level for logging filtered packets
                 logging = config.logging or "off",
                 -- for stats
                 accepted = 0,
                 rejected = 0,
                 dropped = 0,
                 total = 0 }
   assert(obj.logging == "on" or obj.logging == "off",
          ("invalid log level: %s"):format(obj.logging))
   return setmetatable(obj, self)
end

-- called by pfmatch handlers, just drop the packet on the floor
function L7Fw:drop(pkt, len)
   if self.logging == "on" then
      self:log_packet("DROP")
   end

   packet.free(self.current_packet)
   self.dropped = self.dropped + 1
end

-- called by pfmatch handler, handle rejection response
function L7Fw:reject(pkt, len)
   link.transmit(assert(self.output.reject,
                        "output port for reject policy not found"),
                 self:make_reject_response())
   self.rejected = self.rejected + 1

   if self.logging == "on" then
      self:log_packet("REJECT")
   end

   packet.free(self.current_packet)
end

-- called by pfmatch handler, forward packet
function L7Fw:accept(pkt, len)
   link.transmit(self.output.output, self.current_packet)
   self.accepted = self.accepted + 1
end

function L7Fw:push()
   local i       = assert(self.input.input, "input port not found")
   local o       = assert(self.output.output, "output port not found")
   local rules   = self.rules
   local scanner = self.scanner

   while not link.empty(i) do
      local pkt  = link.receive(i)
      local flow = scanner:get_flow(pkt)<commit_msg>Move assert statement earlier in the call graph<commit_after>module(..., package.seeall)

-- This module implements a level 7 firewall app that consumes the result
-- of DPI scanning done by l7spy.
--
-- The firewall rules are a table mapping protocol names to either
--   * a simple action ("drop", "reject", "accept")
--   * a pfmatch expression

local ffi      = require("ffi")
local link     = require("core.link")
local packet   = require("core.packet")
local datagram = require("lib.protocol.datagram")
local ether    = require("lib.protocol.ethernet")
local icmp     = require("lib.protocol.icmp.header")
local ipv4     = require("lib.protocol.ipv4")
local ipv6     = require("lib.protocol.ipv6")
local tcp      = require("lib.protocol.tcp")
local match    = require("pf.match")

ffi.cdef[[
  void syslog(int priority, const char*format, ...);
]]

L7Fw = {}
L7Fw.__index = L7Fw

-- create a new firewall app object given an instance of Scanner
-- and firewall rules
function L7Fw:new(config)
   local obj = { local_ipv4 = config.local_ipv4,
                 local_ipv6 = config.local_ipv6,
                 local_macaddr = config.local_macaddr,
                 scanner = config.scanner,
                 rules = config.rules,
                 -- this map tracks flows to compiled pfmatch functions
                 -- so that we only compile them once per flow
                 handler_map = {},
                 -- log level for logging filtered packets
                 logging = config.logging or "off",
                 -- for stats
                 accepted = 0,
                 rejected = 0,
                 dropped = 0,
                 total = 0 }
   assert(obj.logging == "on" or obj.logging == "off",
          ("invalid log level: %s"):format(obj.logging))
   return setmetatable(obj, self)
end

-- called by pfmatch handlers, just drop the packet on the floor
function L7Fw:drop(pkt, len)
   if self.logging == "on" then
      self:log_packet("DROP")
   end

   packet.free(self.current_packet)
   self.dropped = self.dropped + 1
end

-- called by pfmatch handler, handle rejection response
function L7Fw:reject(pkt, len)
   link.transmit(self.output.reject, self:make_reject_response())
   self.rejected = self.rejected + 1

   if self.logging == "on" then
      self:log_packet("REJECT")
   end

   packet.free(self.current_packet)
end

-- called by pfmatch handler, forward packet
function L7Fw:accept(pkt, len)
   link.transmit(self.output.output, self.current_packet)
   self.accepted = self.accepted + 1
end

function L7Fw:push()
   local i       = assert(self.input.input, "input port not found")
   local o       = assert(self.output.output, "output port not found")
   local rules   = self.rules
   local scanner = self.scanner

   assert(self.output.reject, "output port for reject policy not found")

   while not link.empty(i) do
      local pkt  = link.receive(i)
      local flow = scanner:get_flow(pkt)
{'fraction_non_alphanumeric': 0.10644831115660185, 'fraction_numerical': 0.008529512111907198, 'mean_word_length': 2.4940405244338497, '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': '4296141', 'n_tokens_mistral': 1758, 'n_tokens_neox': 1738, 'n_words': 527}