From 40eeadf387f76607f593902e755ea8fda8eca443 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Fri, 20 Jan 2017 15:16:55 -0800 Subject: [PATCH] Allow pfmatch actions for l7fw rules --- src/apps/wall/l7fw.lua | 59 +++++++++++++++++++++++++----- src/program/wall/filter/filter.lua | 2 +- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/apps/wall/l7fw.lua b/src/apps/wall/l7fw.lua index ecea2e627a..cd3e417faf 100644 --- a/src/apps/wall/l7fw.lua +++ b/src/apps/wall/l7fw.lua @@ -1,22 +1,58 @@ 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 link = require("core.link") local packet = require("core.packet") +local match = require("pf.match") L7Fw = {} L7Fw.__index = L7Fw -- create a new firewall app object given an instance of Scanner -- and firewall rules --- FIXME: for now, the rules are just a Lua table mapping a --- protocol name to a policy "accept" or "drop" --- we may want to have a real rule language, plus pflua --- integration(?) function L7Fw:new(config) - local obj = { scanner = config.scanner, rules = config.rules } + local obj = { scanner = config.scanner } + local rules = {} + + for protocol, action in pairs(config.rules) do + if action == "accept" or action == "drop" or action == "reject" then + rules[protocol] = action + else + rules[protocol] = "pfmatch" + -- TODO: this may be compiling too early for this to work + self["handle_" .. protocol] = match.compile(action) + end + end + + obj.rules = rules + return setmetatable(obj, self) end +-- called by pfmatch handlers, just drop the packet on the floor +function L7Fw:drop(pkt, len) + packet.free(self.current_packet) + return +end + +-- called by pfmatch handler, handle rejection response +function L7Fw:reject(pkt, len) + -- TODO: implement ICMP/TCP RST response + packet.free(self.current_packet) + return +end + +-- called by pfmatch handler, forward packet +function L7Fw:accept(pkt, len) + link.transmit(self.output.output, self.current_packet) +end + function L7Fw:push() local i = assert(self.input.input, "input port not found") local o = assert(self.output.output, "output port not found") @@ -27,17 +63,22 @@ function L7Fw:push() local pkt = link.receive(i) local flow = scanner:get_flow(pkt) + -- so that pfmatch handler methods can access the original packet + self.current_packet = pkt + if flow then local name = scanner:protocol_name(flow.protocol) local policy = rules[name] or rules["default"] - if policy == "accept" then - link.transmit(o, pkt) + if policy == "pfmatch" then + self["handle_" .. name](self, pkt.data, pkt.length) + elseif policy == "accept" then + self:accept(pkt.data, pkt.length) elseif policy == "drop" then - packet.free(pkt) + self:drop(pkt.data, pkt.length) -- TODO: what should the default policy be if there is none specified? else - link.transmit(o, pkt) + self:accept(pkt.data, pkt.length) end else -- TODO: we may wish to have a default policy for packets diff --git a/src/program/wall/filter/filter.lua b/src/program/wall/filter/filter.lua index ffd00e70c5..74ac89ac75 100644 --- a/src/program/wall/filter/filter.lua +++ b/src/program/wall/filter/filter.lua @@ -20,7 +20,7 @@ function run (args) config.app(c, "l7spy", require("apps.wall.l7spy").L7Spy, { scanner = scanner }) config.app(c, "sink", pcap.PcapWriter, out_file) -- TODO: hardcoded some rules here for this experiment - local rules = { RTP = "drop", default = "accept" } + local rules = { RTP = [[match { udp => accept; otherwise => drop }]], default = "drop" } config.app(c, "l7fw", require("apps.wall.l7fw").L7Fw, { scanner = scanner, rules = rules }) config.link(c, "source.output -> l7spy.south") config.link(c, "l7spy.north -> l7fw.input")