Skip to content

Commit

Permalink
Import lightweight 4-over-6 AFTR
Browse files Browse the repository at this point in the history
Imported from Igalia/lwaftr_starfruit, up to date with commit
a87632f
  • Loading branch information
Katerina Barone-Adesi authored and wingo committed Mar 4, 2016
1 parent 88bc0e7 commit 8364729
Show file tree
Hide file tree
Showing 343 changed files with 14,394 additions and 0 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ network.
You can deploy Snabb NFV stand-alone with QEMU or you can integrate it
with a cloud computing platform such as OpenStack.

### lwAFTR

[Snabb lwAFTR](src/program/lwaftr/) is the internet-facing component of
"lightweight 4-over-6" (lw4o6), an IPv6 transition technology. An ISP
can use lwAFTR functions to provide its users with access to the IPv4
internet while maintaining a simple IPv6-only internal network. An ISP
deploying Snabb lwAFTR can also configure lw4o6 to share IPv4 addresses
between multiple different customers, ameliorating the IPv4 address
space exhaustion problem and lowering costs. See the [lwAFTR
documentation](src/program/lwaftr/doc/) for more details.

### VPWS

VPWS (Virtual Private Wire Service) is a Layer-2 VPN application being
Expand Down
133 changes: 133 additions & 0 deletions src/apps/lwaftr/benchmark.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
-- Source -> NIC1 -> NIC2 -> Sink

local Intel82599 = require("apps.intel.intel_app").Intel82599
local PcapReader = require("apps.pcap.pcap").PcapReader
local basic_apps = require("apps.basic.basic_apps")
local counter = require("core.counter")
local ffi = require("ffi")
local lib = require("core.lib")
local lwaftr = require("apps.lwaftr.lwaftr")

local C = ffi.C

local paths = {
["nicv4-in"] = "nicv4.input.rx",
["nicv6-in"] = "nicv6.input.rx",
["nicv4-out"] = "nicv4.output.tx",
["nicv6-out"] = "nicv6.output.tx",
["lwaftrv4-out"] = "lwaftr.output.v4",
["lwaftrv6-out"] = "lwaftr.output.v6",
["lwaftrv4-in"] = "lwaftr.input.v4",
["lwaftrv6-in"] = "lwaftr.input.v6",
}

local function split (str, sep)
local t = {}
local regex = ("([^%s]+)"):format(sep)
for each in str:gmatch(regex) do
table.insert(t, each)
end
return t
end

local function bench(engine, params)
local function format (str, t)
for key, _ in str:gmatch("{([a-zA-Z_]+)}") do
str = str:gsub("{"..key.."}", t[key])
end
return str
end
local function report (name, breaths, bytes, packets, runtime)
local values = {
name = name,
breath_in_nanosecond = ("%.2f"):format(runtime / breaths * 1e6),
breaths = lib.comma_value(breaths),
bytes = bytes,
million_packets = ("%.1f"):format(packets / 1e6),
packets_per_breath = ("%.2f"):format(packets / breaths),
rate_gbps = ("%.2f"):format((bytes * 8 ) / 1e9 / runtime),
rate_mpps = ("%.3f"):format(packets / runtime / 1e6),
runtime = ("%.2f"):format(runtime),
}
print("\n"..format([[{name} processed {million_packets} million packets in {runtime} seconds ({bytes} bytes; {rate_gbps} Gbps)
Made {breaths} breaths: {packets_per_breath} packets per breath; {breath_in_nanosecond} us per breath
Rate(Mpps): {rate_mpps}
]], values))
end
local function report_bench(input, name, engine, finish, start)
local breaths = tonumber(counter.read(engine.breaths))
local bytes = input.txbytes
-- Don't bother to report on interfaces that were boring
if bytes == 0 then return end
local packets = input.txpackets
local runtime = finish - start
report(name, breaths, bytes, packets, runtime)
end
local function reports(names, engine, finish, start)
for _, name in ipairs(names) do
local parts = split(paths[name], ".")
assert(#parts == 3, "Wrong path")
local app_name, channel, direction = unpack(parts)
local stats = link.stats(engine.app_table[app_name][channel][direction])
report_bench(stats, name, engine, finish, start)
end
end
local start = C.get_monotonic_time()
engine.main(params)
local finish = C.get_monotonic_time()
reports({"nicv4-in","nicv6-in"}, engine, finish, start)
end

local function usage ()
print([[
Usage: <conf_file> <pcap_file_v4> <pcap_file_v6> <pci_dev_v4> <pci_dev_v6>
<conf_file>: Path to lwaftr configuration file.
<pcap_file_v4>: Path to pcap file contain IPv4 packet/s to be sent.
<pcap_file_v6>: Path to pcap file contain IPv6 packet/s to be sent.
<pci_dev_v4>: PCI ID number of network card for IPv4.
<pci_dev_v6>: PCI ID number of network card for IPv6.
]])
os.exit()
end

local function testInternalLoopbackFromPcapFile (params)
if #params ~= 5 then usage() end
local conf_file, pcapv4_file, pcapv6_file, pcidev_v4, pcidev_v6 = unpack(params)

engine.configure(config.new())
local c = config.new()
config.app(c, 'lwaftr', lwaftr.LwAftr, conf_file)
config.app(c, 'pcapv4', PcapReader, pcapv4_file)
config.app(c, 'pcapv6', PcapReader, pcapv6_file)
config.app(c, 'repeater_v4', basic_apps.Repeater)
config.app(c, 'repeater_v6', basic_apps.Repeater)
config.app(c, 'sink', basic_apps.Sink)

-- Both nics are full-duplex
config.app(c, 'nicv4', Intel82599, {
pciaddr = pcidev_v4,
macaddr = '22:22:22:22:22:22',
})

config.app(c, 'nicv6', Intel82599, {
pciaddr = pcidev_v6,
macaddr = '44:44:44:44:44:44',
})

config.link(c, 'pcapv4.output -> repeater_v4.input')
config.link(c, 'repeater_v4.output -> lwaftr.v4')
config.link(c, 'lwaftr.v4 -> nicv4.rx')
config.link(c, 'nicv4.tx -> sink.in1')

config.link(c, 'pcapv6.output -> repeater_v6.input')
config.link(c, 'repeater_v6.output -> lwaftr.v6')
config.link(c, 'lwaftr.v6 -> nicv6.rx')
config.link(c, 'nicv6.tx -> sink.in1')

engine.configure(c)

bench(engine, {duration=5, report={showlinks=true}})
end

testInternalLoopbackFromPcapFile(main.parameters)
122 changes: 122 additions & 0 deletions src/apps/lwaftr/binary_search.dasl
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
-- Optimized branchless binary search over sorted vectors -*- lua -*-
--
-- An optimized implementation of branchless binary search, following
-- the article by Paul Khuoung, "Binary search *eliminates* branch
-- misprediction.":
--
-- http://www.pvk.ca/Blog/2012/07/03/binary-search-star-eliminates-star-branch-mispredictions/

module(..., package.seeall)

local debug = false

local ffi = require("ffi")
local bit = require("bit")
local C = ffi.C

local dasm = require("dasm")

|.arch x64
|.actionlist actions

-- Table keeping machine code alive to the GC.
local anchor = {}

-- Utility: assemble code and optionally dump disassembly.
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

function gen(count, entry_type)
local function gen_binary_search(Dst)
if count == 1 then
| mov rax, rdi
| ret
return
end

local entry_byte_size = ffi.sizeof(entry_type)
local size = 1
while size < count do size = size * 2 end

-- Initially, the vector is in edi and the key we are looking for
-- is in esi. Save the vector pointer in rdx.
| mov rdx, rdi

-- In the first bisection, make sure the rest of the bisections
-- have a power-of-two size.
do
local next_size = size / 2
local mid = next_size - 1
local mid_offset = mid * entry_byte_size
local hi_offset = (count - next_size) * entry_byte_size
| cmp [rdi + mid_offset], esi
| lea rax, [rdi + hi_offset]
| cmovb rdi, rax
size = size / 2
end

-- In the rest, just burn down the halves. Wheeee!
while size > 1 do
local next_size = size / 2
local mid = next_size - 1
local mid_offset = mid * entry_byte_size
local hi_offset = next_size * entry_byte_size
| cmp [rdi + mid_offset], esi
| lea rax, [rdi + hi_offset]
| cmovb rdi, rax
size = next_size
end

-- Now rdi points at the answer (if we have one). Done!
| mov rax, rdi
| ret
end
return assemble("binary_search_"..count,
ffi.typeof("$*(*)($*, uint32_t)", entry_type, entry_type),
gen_binary_search)
end

function selftest ()
print("selftest: binary_search")
local test = ffi.new('uint32_t[15]',
{ 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5 })
local searchers = {}
for i=1,10 do searchers[i] = gen(i, ffi.typeof('uint32_t')) end

local function assert_search(size, key, expected)
local res = searchers[size](test, key) - test
if res ~= expected then
error(('in search of size %d for key %d: expected %d, got %d'):format(
size, key, expected, res))
end
end

for i=1,10 do
assert_search(i, 0, 0)
assert_search(i, 1, 0)
assert_search(i, 6, i - 1)
end

for i=2,10 do
assert_search(i, 2, 1)
end

for i=4,10 do
assert_search(i, 3, 3)
end

for i=7,10 do
assert_search(i, 4, 6)
end

print("selftest: ok")
end
Loading

0 comments on commit 8364729

Please sign in to comment.