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

Merge max-next for v2018.05 #1350

Merged
merged 16 commits into from
Jun 19, 2018
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
46 changes: 44 additions & 2 deletions src/apps/pcap/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# PcapReader and PcapWriter Apps (apps.pcap.pcap)
# Pcap Savefile Apps

## PcapReader and PcapWriter Apps (apps.pcap.pcap)

The `PcapReader` and `PcapWriter` apps can be used to inject and log raw
packet data into and out of the app network using the
Expand All @@ -14,10 +16,50 @@ port to a PCAP file.
| | | |
+------------+ +------------+

## Configuration
### Configuration

Both `PcapReader` and `PcapWriter` expect a filename string as their
configuration arguments to read from and write to respectively. `PcapWriter`
will alternatively accept an array as its configuration argument, with the
first element being the filename and the second element being a *mode* argument
to `io.open`.

## Tap (apps.pcap.tap)

The `Tap` app is a simple in-band packet tap that writes packets that it
sees to a pcap savefile. It can optionally only write packets that pass
a pcap filter, and optionally subsample so it can write only every /n/th
packet.

DIAGRAM: pcaptap
+-------------------+
input | | output
---->* apps.pcap.tap.Tap *---->
| |
+-------------------+

### Configuration

The `Tap` app accepts a table as its configuration argument. The
following keys are defined:

— Key **filename**

*Required*. The name of the file to which to write the packets.

— Key **mode**

*Optional*. Either `"truncate"` or `"append"`, indicating whether the
savefile will be truncated (the default) or appended to.

— Key **filter**

*Optional*. A pflang filter expression to select packets for tapping.
Only packets that pass this filter will be sampled for the packet tap.

— Key **sample**

*Optional*. A sampling period. Defaults to 1, indicating that every
packet seen by the tap and passing the optional filter string will be
written. Setting this value to 2 will capture every second packet, and
so on.
87 changes: 87 additions & 0 deletions src/apps/pcap/tap.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
-- Use of this source code is governed by the Apache 2.0 license; see COPYING.

module(...,package.seeall)

local ffi = require("ffi")

local app = require("core.app")
local lib = require("core.lib")
local link = require("core.link")
local pcap = require("lib.pcap.pcap")
local pf = require("pf")

Tap = {}

local tap_config_params = {
-- Name of file to which to write packets.
filename = { required=true },
-- "truncate" to truncate the file, or "append" to add to the file.
mode = { default = "truncate" },
-- Only packets that match this pflang filter will be captured.
filter = { },
-- Only write every Nth packet that matches the filter.
sample = { default=1 },
}

function Tap:new(conf)
local o = lib.parse(conf, tap_config_params)
local mode = assert(({truncate='w+b', append='a+b'})[o.mode])
o.file = assert(io.open(o.filename, mode))
if o.file:seek() == 0 then pcap.write_file_header(o.file) end
if o.filter then o.filter = pf.compile_filter(o.filter) end
o.n = o.sample - 1
return setmetatable(o, {__index = Tap})
end

function Tap:push ()
local n = self.n
while not link.empty(self.input.input) do
local p = link.receive(self.input.input)
if not self.filter or self.filter(p.data, p.length) then
n = n + 1
if n == self.sample then
n = 0
pcap.write_record(self.file, p.data, p.length)
end
end
link.transmit(self.output.output, p)
end
self.n = n
end

function selftest ()
print('selftest: apps.pcap.tap')

local config = require("core.config")
local Sink = require("apps.basic.basic_apps").Sink
local PcapReader = require("apps.pcap.pcap").PcapReader

local function run(filter, sample)
local tmp = os.tmpname()
local c = config.new()
-- Re-use example from packet filter test.
config.app(c, "source", PcapReader, "apps/packet_filter/samples/v6.pcap")
config.app(c, "tap", Tap, {filename=tmp, filter=filter, sample=sample})
config.app(c, "sink", Sink )

config.link(c, "source.output -> tap.input")
config.link(c, "tap.output -> sink.input")
app.configure(c)
while not app.app_table.source.done do app.breathe() end

local n = 0
for packet, record in pcap.records(tmp) do n = n + 1 end
os.remove(tmp)

app.configure(config.new())

return n
end

assert(run() == 161)
assert(run("icmp6") == 49)
assert(run(nil, 2) == 81)
assert(run("icmp6", 2) == 25)

print('selftest: ok')
end
Loading