Skip to content

Commit

Permalink
Merge pull request snabbco#15 from takikawa/snabbwall-dev
Browse files Browse the repository at this point in the history
Optimizations for snabbwall & benchmarking script
  • Loading branch information
takikawa committed Feb 23, 2017
2 parents 15a90b8 + 0e21baf commit a157576
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 75 deletions.
54 changes: 54 additions & 0 deletions src/apps/wall/constants.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
module(..., package.seeall)

local lib = require("core.lib")

-- Constants: Ethernet
ETH_TYPE_IPv4 = lib.htons(0x0800)
ETH_TYPE_IPv6 = lib.htons(0x86DD)
ETH_TYPE_VLAN = lib.htons(0x8100)
ETH_TYPE_OFFSET = 12
ETH_HEADER_SIZE = 14

-- Constants: IPv4
IPv4_VER_IHL_OFFSET = 0
IPv4_DSCP_ECN_OFFSET = 1
IPv4_LEN_OFFSET = 2
IPv4_FRAG_ID_OFFSET = 4
IPv4_FLAGS_OFFSET = 6
IPv4_TTL_OFFSET = 8
IPv4_PROTO_OFFSET = 9
IPv4_CHECKSUM_OFFSET = 10
IPv4_SRC_ADDR_OFFSET = 12
IPv4_DST_ADDR_OFFSET = 16

IPv4_PROTO_TCP = 6 -- uint8_t
IPv4_PROTO_UDP = 17 -- uint8_t

-- Constants: IPv6
IPv6_MIN_HEADER_SIZE = 40
IPv6_PLOADLEN_OFFSET = 4
IPv6_NEXTHDR_OFFSET = 6
IPv6_HOPLIMIT_OFFSET = 7
IPv6_SRC_ADDR_OFFSET = 8
IPv6_DST_ADDR_OFFSET = 24

IPv6_NEXTHDR_HOPBYHOP= 0
IPv6_NEXTHDR_TCP = 6
IPv6_NEXTHDR_UDP = 17
IPv6_NEXTHDR_ROUTING = 43
IPv6_NEXTHDR_FRAGMENT= 44
IPv6_NEXTHDR_ESP = 50
IPv6_NEXTHDR_AH = 51
IPv6_NEXTHDR_ICMPv6 = 58
IPv6_NEXTHDR_NONE = 59
IPv6_NEXTHDR_DSTOPTS = 60

-- Constants: TCP
TCP_HEADER_SIZE = 20
TCP_SRC_PORT_OFFSET = 0
TCP_DST_PORT_OFFSET = 2

-- Constants: UDP
UDP_HEADER_SIZE = 8
UDP_SRC_PORT_OFFSET = 0
UDP_DST_PORT_OFFSET = 2
94 changes: 36 additions & 58 deletions src/apps/wall/scanner.lua
Original file line number Diff line number Diff line change
@@ -1,66 +1,40 @@
module(..., package.seeall)

local util = require("apps.wall.util")
local lib = require("core.lib")
local bit = require("bit")
local ffi = require("ffi")
local util = require("apps.wall.util")
local const = require("apps.wall.constants")
local lib = require("core.lib")
local bit = require("bit")
local ffi = require("ffi")

local rd16, rd32 = util.rd16, util.rd32
local ipv4_addr_cmp, ipv6_addr_cmp = util.ipv4_addr_cmp, util.ipv6_addr_cmp
local tobit, lshift, rshift = bit.tobit, bit.lshift, bit.rshift
local band, bxor, bnot = bit.band, bit.bxor, bit.bnot

-- Constants: Ethernet
ETH_TYPE_IPv4 = lib.htons(0x0800)
ETH_TYPE_IPv6 = lib.htons(0x86DD)
ETH_TYPE_VLAN = lib.htons(0x8100)
ETH_TYPE_OFFSET = 12
ETH_HEADER_SIZE = 14

-- Constants: IPv4
IPv4_VER_IHL_OFFSET = 0
IPv4_DSCP_ECN_OFFSET = 1
IPv4_LEN_OFFSET = 2
IPv4_FRAG_ID_OFFSET = 4
IPv4_FLAGS_OFFSET = 6
IPv4_TTL_OFFSET = 8
IPv4_PROTO_OFFSET = 9
IPv4_CHECKSUM_OFFSET = 10
IPv4_SRC_ADDR_OFFSET = 12
IPv4_DST_ADDR_OFFSET = 16

IPv4_PROTO_TCP = 6 -- uint8_t
IPv4_PROTO_UDP = 17 -- uint8_t

-- Constants: IPv6
IPv6_MIN_HEADER_SIZE = 40
IPv6_PLOADLEN_OFFSET = 4
IPv6_NEXTHDR_OFFSET = 6
IPv6_HOPLIMIT_OFFSET = 7
IPv6_SRC_ADDR_OFFSET = 8
IPv6_DST_ADDR_OFFSET = 24

IPv6_NEXTHDR_HOPBYHOP= 0
IPv6_NEXTHDR_TCP = 6
IPv6_NEXTHDR_UDP = 17
IPv6_NEXTHDR_ROUTING = 43
IPv6_NEXTHDR_FRAGMENT= 44
IPv6_NEXTHDR_ESP = 50
IPv6_NEXTHDR_AH = 51
IPv6_NEXTHDR_ICMPv6 = 58
IPv6_NEXTHDR_NONE = 59
IPv6_NEXTHDR_DSTOPTS = 60

-- Constants: TCP
TCP_HEADER_SIZE = 20
TCP_SRC_PORT_OFFSET = 0
TCP_DST_PORT_OFFSET = 2

-- Constants: UDP
UDP_HEADER_SIZE = 8
UDP_SRC_PORT_OFFSET = 0
UDP_DST_PORT_OFFSET = 2

local ETH_TYPE_IPv4 = const.ETH_TYPE_IPv4
local ETH_TYPE_IPv6 = const.ETH_TYPE_IPv6
local ETH_TYPE_VLAN = const.ETH_TYPE_VLAN
local ETH_TYPE_OFFSET = const.ETH_TYPE_OFFSET
local ETH_HEADER_SIZE = const.ETH_HEADER_SIZE
local IPv4_PROTO_OFFSET = const.IPv4_PROTO_OFFSET
local IPv4_SRC_ADDR_OFFSET = const.IPv4_SRC_ADDR_OFFSET
local IPv4_DST_ADDR_OFFSET = const.IPv4_DST_ADDR_OFFSET
local IPv4_PROTO_TCP = const.IPv4_PROTO_TCP
local IPv4_PROTO_UDP = const.IPv4_PROTO_UDP
local IPv6_NEXTHDR_OFFSET = const.IPv6_NEXTHDR_OFFSET
local IPv6_SRC_ADDR_OFFSET = const.IPv6_SRC_ADDR_OFFSET
local IPv6_DST_ADDR_OFFSET = const.IPv6_DST_ADDR_OFFSET
local IPv6_NEXTHDR_HOPBYHOP = const.IPv6_NEXTHDR_HOPBYHOP
local IPv6_NEXTHDR_TCP = const.IPv6_NEXTHDR_TCP
local IPv6_NEXTHDR_UDP = const.IPv6_NEXTHDR_UDP
local IPv6_NEXTHDR_ROUTING = const.IPv6_NEXTHDR_ROUTING
local IPv6_NEXTHDR_FRAGMENT = const.IPv6_NEXTHDR_FRAGMENT
local IPv6_NEXTHDR_AH = const.IPv6_NEXTHDR_AH
local IPv6_NEXTHDR_NONE = const.IPv6_NEXTHDR_NONE
local IPv6_NEXTHDR_DSTOPTS = const.IPv6_NEXTHDR_DSTOPTS
local TCP_SRC_PORT_OFFSET = const.TCP_SRC_PORT_OFFSET
local TCP_DST_PORT_OFFSET = const.TCP_DST_PORT_OFFSET
local UDP_SRC_PORT_OFFSET = const.UDP_SRC_PORT_OFFSET
local UDP_DST_PORT_OFFSET = const.UDP_DST_PORT_OFFSET

ffi.cdef [[
struct swall_flow_key_ipv4 {
Expand Down Expand Up @@ -119,13 +93,17 @@ local flow_key_ipv4 = ffi.metatype("struct swall_flow_key_ipv4", {
}
})

local the_flow_key_ipv4 = flow_key_ipv4()

local flow_key_ipv6 = ffi.metatype("struct swall_flow_key_ipv6", {
__index = {
hash = make_cdata_hash_function(ffi.sizeof("struct swall_flow_key_ipv6")),
eth_type = function (self) return ETH_TYPE_IPv6 end,
}
})

local the_flow_key_ipv6 = flow_key_ipv6()

-- Helper functions

--
Expand Down Expand Up @@ -199,7 +177,7 @@ function Scanner:extract_packet_info(p)

local key, src_addr, src_port, dst_addr, dst_port, ip_proto
if eth_type == ETH_TYPE_IPv4 then
key = flow_key_ipv4()
key = the_flow_key_ipv4
src_addr = p.data + ip_offset + IPv4_SRC_ADDR_OFFSET
dst_addr = p.data + ip_offset + IPv4_DST_ADDR_OFFSET
if ipv4_addr_cmp(src_addr, dst_addr) <= 0 then
Expand All @@ -220,7 +198,7 @@ function Scanner:extract_packet_info(p)
dst_port = rd16(p.data + ip_payload_offset + UDP_DST_PORT_OFFSET)
end
elseif eth_type == ETH_TYPE_IPv6 then
key = flow_key_ipv6()
key = the_flow_key_ipv6
src_addr = p.data + ip_offset + IPv6_SRC_ADDR_OFFSET
dst_addr = p.data + ip_offset + IPv6_DST_ADDR_OFFSET
if ipv6_addr_cmp(src_addr, dst_addr) <= 0 then
Expand Down
34 changes: 21 additions & 13 deletions src/apps/wall/scanner/ndpi.lua
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
local scanner = require("apps.wall.scanner")
local const = require("apps.wall.constants")
local opt = require("apps.wall.scanner.ndpi_opt")
local util = require("apps.wall.util")
local ndpi = require("ndpi")

local rd32, ipv4_addr_cmp, ipv6_addr_cmp = util.rd32, util.ipv4_addr_cmp, util.ipv6_addr_cmp
local ETH_TYPE_IPv4 = const.ETH_TYPE_IPv4
local ETH_TYPE_IPv6 = const.ETH_TYPE_IPv6
local IPv4_PROTO_UDP = const.IPv4_PROTO_UDP
local IPv4_PROTO_TCP = const.IPv4_PROTO_TCP

local NdpiFlow = subClass()
NdpiFlow._name = "SnabbWall nDPI Flow"
Expand Down Expand Up @@ -81,14 +87,14 @@ function NdpiScanner:scan_packet(p, time)
end

local src_id, dst_id = flow._ndpi_src_id, flow._ndpi_dst_id
if key:eth_type() == scanner.ETH_TYPE_IPv4 then
if key:eth_type() == ETH_TYPE_IPv4 then
if ipv4_addr_cmp(src_addr, key.lo_addr) ~= 0 or
ipv4_addr_cmp(dst_addr, key.hi_addr) ~= 0 or
src_port ~= key.lo_port or dst_port ~= key.hi_port
then
src_id, dst_id = dst_id, src_id
end
elseif key:eth_type() == scanner.ETH_TYPE_IPv6 then
elseif key:eth_type() == ETH_TYPE_IPv6 then
if ipv6_addr_cmp(src_addr, key.lo_addr) ~= 0 or
ipv6_addr_cmp(dst_addr, key.hi_addr) ~= 0 or
src_port ~= key.lo_port or dst_port ~= key.hi_port
Expand All @@ -98,12 +104,13 @@ function NdpiScanner:scan_packet(p, time)
end

flow.proto_master, flow.protocol =
self._ndpi:process_packet(flow._ndpi_flow,
p.data + ip_offset,
p.length - ip_offset,
time,
src_id,
dst_id)
opt.process_packet(self._ndpi,
flow._ndpi_flow,
p.data + ip_offset,
p.length - ip_offset,
time,
src_id,
dst_id)

if flow.protocol ~= ndpi.protocol.PROTOCOL_UNKNOWN then
return true, flow
Expand All @@ -112,13 +119,14 @@ function NdpiScanner:scan_packet(p, time)
-- TODO: Check and tune-up the constants for number of packets
-- TODO: Do similarly for IPv6 packets once nDPI supports using IPv6
-- addresses here (see https://github.com/ntop/nDPI/issues/183)
if (flow.key.ip_proto == scanner.IPv4_PROTO_UDP and flow.packets > 8) or
(flow.key.ip_proto == scanner.IPv4_PROTO_TCP and flow.packets > 10)
if (flow.key.ip_proto == IPv4_PROTO_UDP and flow.packets > 8) or
(flow.key.ip_proto == IPv4_PROTO_TCP and flow.packets > 10)
then
flow.proto_master, flow.protocol =
self._ndpi:guess_undetected_protocol(flow.key.ip_proto,
rd32(src_addr), src_port,
rd32(dst_addr), dst_port)
opt.guess_undetected_protocol(self._ndpi,
flow.key.ip_proto,
rd32(src_addr), src_port,
rd32(dst_addr), dst_port)
-- TODO: Check whether we should check again for PROTOCOL_UNKNOWN
return true, flow
end
Expand Down
103 changes: 103 additions & 0 deletions src/apps/wall/scanner/ndpi_opt.dasl
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
-- This module creates a wrapper around nDPI's packet processing
-- functions in order to help LuaJIT do its optimizations
--
-- Specifically, it avoids a struct value return by creating wrappers
-- that pass a struct pointer in instead. LuaJIT can handle that
-- better than a struct return (which is not supported).

module(..., package.seeall)

local dasm = require("dasm")
local ffi = require("ffi")
local ndpi = require("ndpi.c").lib

local debug = false

|.arch x64
|.actionlist actions

-- the definitions here (anchor, assemble, gen) are borrowed from lwaftr
-- (see multi_copy.lua)
__anchor = {}

local function assemble (name, prototype, generator)
local Dst = dasm.new(actions)
generator(Dst)
local mcode, size = Dst:build()
table.insert(__anchor, mcode)
if debug then
print("mcode dump: "..name)
dasm.dump(mcode, size)
end
return ffi.cast(prototype, mcode)
end

local function gen_ndppw(orig_f)
local function gen(Dst)
-- pass the first stack argument onto the original function
| mov rax, [rsp+8]
| push rax

-- call the original function, do stack cleanup
| mov64 rax, orig_f
| call rax
| add rsp, 8

-- at this point, rax and rdx have struct
-- fields in them, which we want to write into
-- the struct pointer (2nd stack arg)
| mov rcx, [rsp+16]
| mov [rcx], rax
| mov [rcx+4], rdx

| ret
end

return gen
end

local function gen_nupw(orig_f)
local function gen(Dst)
-- call the original function, aligning on 16
| sub rsp, 8
| mov64 rax, orig_f
| call rax
| add rsp, 8

-- like above, write into struct
| mov rcx, [rsp+8]
| mov [rcx], rax
| mov [rcx+4], rdx

| ret
end

return gen
end

-- see ljndpi/ndpi/c.lua for the corresponding headers for these functions
-- these have an extra void* argument at the end for the struct pointer
local function make_ndpp_wrapper(f)
local wrap = assemble("ndpi_detection_process_packet_wrapper",
ffi.typeof("void (*)(void*, void*, void*, unsigned short, uint64_t, void*, void*, void*)"),
gen_ndppw(f))
return function(self, flow, data, len, tick, src, dst)
local proto = ffi.new("ndpi_protocol_t")
wrap(self, flow, data, len, tick, src, dst, proto)
return proto.master_protocol, proto.protocol
end
end

local function make_ngup_wrapper(f)
local wrap = assemble("ndpi_detection_process_packet_wrapper",
ffi.typeof("void (*)(void*, uint8_t, uint32_t, uint16_t, uint32_t, uint16_t, void*)"),
gen_nupw(f))
return function(self, prot_n, src_h, src_p, dst_h, dst_p)
local proto = ffi.new("ndpi_protocol_t")
wrap(self, prot_n, src_h, src_p, dst_h, dst_p, proto)
return proto.master_protocol, proto.protocol
end
end

process_packet = make_ndpp_wrapper(ndpi.ndpi_detection_process_packet)
guess_undetected_protocol = make_ngup_wrapper(ndpi.ndpi_guess_undetected_protocol)
1 change: 1 addition & 0 deletions src/program/wall/filter/README
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ Options:
-4, --ipv4 <ip_addr> Set the IPv4 address of this firewall host
-6, --ipv6 <ip_addr> Set the IPv6 address of this firewall host
-D, --duration <secs> Set the duration to run the program (in seconds).
--cpu <cpu-num> Pin to a particular CPU and appropriate NUMA node
Loading

0 comments on commit a157576

Please sign in to comment.