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 ipsec into next (2018.10) #1382

Merged
merged 33 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
59b1b82
apps.ipsec: add tunnel mode esp app
eugeneia Mar 29, 2018
fd2bcfb
Merge branch 'master' into ipsec
eugeneia Apr 2, 2018
17e8c6c
snabbmark esp: add rudimentary PMU analysis
eugeneia Apr 10, 2018
461f195
lib.ipsec.aes_128_gcm: add rudimentary PMU profiling to selftest
eugeneia Apr 10, 2018
9d14b98
snabbmark esp: fix bug where packets of wrong size were constructed
eugeneia Apr 2, 2018
7f75dc1
snabbmark esp: minor changes
eugeneia Apr 2, 2018
bb72e66
lib.ipsec: add missing licensing headers
eugeneia Apr 2, 2018
53ae8bd
lib.ipsec.esp: correct bogus note about IP fragment handling
eugeneia Apr 3, 2018
531170a
lib.ipsec.esp: avoid lib.protocol.* on the fast-path
eugeneia Apr 2, 2018
43d498a
lib.ipsec.esp: use full 16 (instead of 12) byte ICV
eugeneia Apr 11, 2018
27dbe3d
Merge branch 'master' into ipsec
eugeneia Jun 1, 2018
846930a
Merge PR #1314 (add tunnel mode esp app) into ipsec
eugeneia Jun 1, 2018
9e270a2
Merge branch 'snabbmark-ipsec-pmu' into ipsec-next
eugeneia Jun 1, 2018
f7e2874
Merge branch 'ipsec-standard-icv' into ipsec-next
eugeneia Jun 1, 2018
820daaf
Merge branch 'ipsec-protocol-overhead' into ipsec-next
eugeneia Jun 1, 2018
66a0dbb
lib.ipsec.esp: keep lib.protocol.esp*
eugeneia Jun 1, 2018
796c177
lib.ipsec.esp: optimize padding by specializing for power of two
eugeneia Jun 12, 2018
5b86f94
lib.ipsec.esp: fix a packet leak in :resync()
eugeneia Sep 10, 2018
94f9b4b
Merge PR #1381 (lib.ipsec: optimization and fixes) into ipsec
eugeneia Sep 13, 2018
c2e4057
Merge branch 'master' into ipsec
eugeneia Sep 13, 2018
24c9a67
Merge PR #1403 (v2019.06 release) into master
eugeneia Jun 6, 2019
554f5a2
Merge branch 'master' into ipsec
eugeneia Jul 17, 2019
817c8d7
core.lib: accept whitespace and error on missing bytes in hexundump
eugeneia Jul 17, 2019
6110b28
apps.keyed_ipv6_tunnel: fix hexundump usage
eugeneia Jul 21, 2019
3cc308d
Merge branch 'picky-hexundump' into ipsec
eugeneia Jul 22, 2019
59c767e
lib.ipsec.aes_128_gcm: use new hexundump capabilities
eugeneia Jul 17, 2019
7304f4b
lib.ipsec.aes_*: add support for AES256
eugeneia Jan 30, 2019
93ffd04
lib.ipsec.aes_*: reorganize, expose AES256-GCM to lib.ipsec.esp
eugeneia Jan 30, 2019
4add842
lib.ipsec.aes_gcm: document usage
eugeneia Mar 13, 2019
fec6c57
lib.ipsec.aes_gcm_avx: support large AAD, extract AAD hashing
eugeneia Jul 17, 2019
51a1389
lib.ipsec.aes_gcm_avx: some edits and clarifications in comments
eugeneia Mar 27, 2019
b4f12f0
apps.ipsec: extend Linux interop test to cover AES256
eugeneia Jul 21, 2019
fa7a61f
Merge branch 'ipsec-aes-gcm-256-aad-prehash' into ipsec
eugeneia Jul 22, 2019
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
7 changes: 5 additions & 2 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -816,9 +816,12 @@ end

Returns hexadecimal string for bytes in *string*.

— Function **lib.hexundump** *hexstring*
— Function **lib.hexundump** *hexstring*, *n*, *error*

Returns byte string for *hexstring*.
Returns string of *n* bytes for *hexstring*. Throws an error if less than *n*
hex-encoded bytes could be parsed unless *error* is `false`.

*Error* is optional and can be the error message to throw.

— Function **lib.comma_value** *n*

Expand Down
50 changes: 32 additions & 18 deletions src/apps/ipsec/README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# IPsec Apps

## AES128gcm (apps.ipsec.esp)

The `AES128gcm` implements ESP in transport mode using the AES-GCM-128
cipher. It encrypts packets received on its `decapsulated` port and transmits
them on its `encapsulated` port, and vice-versa. Packets arriving on the
`decapsulated` port must have an IPv6 header, and packets arriving on the
`encapsulated` port must have an IPv6 header followed by an ESP header,
otherwise they will be discarded.

DIAGRAM: AES128gcm
+-----------+
encapsulated | |
---->* AES128gcm *<----
<----* *---->
| | decapsulated
+-----------+
## ESP Transport6 and Tunnel6 (apps.ipsec.esp)

The `Transport6` and `Tunnel6` apps implement ESP in transport and tunnel mode
respectively. they encrypts packets received on their `decapsulated` port and
transmit them on their `encapsulated` port, and vice-versa. Packets arriving on
the `decapsulated` port must have Ethernet and IPv6 headers, and packets
arriving on the `encapsulated` port must have an Ethernet and IPv6 headers
followed by an ESP header, otherwise they will be discarded.

DIAGRAM: Transport6
+------------+
encapsulated | |
---->* Transport6 *<----
<----* Tunnel6 *---->
| | decapsulated
+------------+

encapsulated
--------\ /----------
Expand All @@ -28,8 +28,22 @@ References:

### Configuration

The `AES128gcm` app accepts a table as its configuration argument. The
following keys are defined:
The `Transport6` and `Tunnel6` apps accepts a table as its configuration
argument. The following keys are defined:

— Key **self_ip** (`Tunnel6` only)

*Required*. Source address of the encapsulating IPv6 header.

— Key **nexthop_ip** (`Tunnel6` only)

*Required*. Destination address of the encapsulating IPv6 header.

— Key **aead**

*Optional*. The identifier of the AEAD to use for encryption and
authentication. For now, only the default `"aes-gcm-16-icv"` (AES-GCM with a 16
octet ICV) is supported.

— Key **spi**

Expand Down
149 changes: 140 additions & 9 deletions src/apps/ipsec/esp.lua
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
-- Use of this source code is governed by the Apache 2.0 license; see COPYING.

-- This app implements a point-to-point encryption tunnel using ESP with
-- AES128GCM12 in transport mode over IPv6.
-- Apps that implements point-to-point ESP tunnels in transport and tunnel mode
-- for IPv6.

module(..., package.seeall)
local esp = require("lib.ipsec.esp")
local counter = require("core.counter")
local C = require("ffi").C
local ethernet = require("lib.protocol.ethernet")
local ipv6 = require("lib.protocol.ipv6")

AES128gcm = {
Transport6 = {
config = {
spi = {required=true},
aead = {default="aes-gcm-16-icv"},
transmit_key = {required=true},
transmit_salt = {required=true},
receive_key = {required=true},
Expand All @@ -25,28 +27,28 @@ AES128gcm = {
}
}

function AES128gcm:new (conf)
function Transport6:new (conf)
local self = {}
assert(conf.transmit_salt ~= conf.receive_salt,
"Refusing to operate with transmit_salt == receive_salt")
self.encrypt = esp.encrypt:new{
mode = "aes-gcm-128-12",
aead = conf.aead,
spi = conf.spi,
key = conf.transmit_key,
salt = conf.transmit_salt}
self.decrypt = esp.decrypt:new{
mode = "aes-gcm-128-12",
aead = conf.aead,
spi = conf.spi,
key = conf.receive_key,
salt = conf.receive_salt,
window_size = conf.receive_window,
resync_threshold = conf.resync_threshold,
resync_attempts = conf.resync_attempts,
auditing = conf.auditing}
return setmetatable(self, {__index = AES128gcm})
return setmetatable(self, {__index = Transport6})
end

function AES128gcm:push ()
function Transport6:push ()
-- Encapsulation path
local input = self.input.decapsulated
local output = self.output.encapsulated
Expand Down Expand Up @@ -74,3 +76,132 @@ function AES128gcm:push ()
end
end
end

Tunnel6 = {
config = {
self_ip = {required=true},
nexthop_ip = {required=true},
spi = {required=true},
aead = {default="aes-gcm-16-icv"},
transmit_key = {required=true},
transmit_salt = {required=true},
receive_key = {required=true},
receive_salt = {required=true},
receive_window = {},
resync_threshold = {},
resync_attempts = {},
auditing = {},
selftest = {default=false}
},
shm = {
txerrors = {counter}, rxerrors = {counter}
},
-- https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
NextHeaderIPv6 = 41
}

function Tunnel6:new (conf)
local self = {}
assert(conf.selftest or conf.transmit_salt ~= conf.receive_salt,
"Refusing to operate with transmit_salt == receive_salt")
self.encrypt = esp.encrypt:new{
aead = conf.aead,
spi = conf.spi,
key = conf.transmit_key,
salt = conf.transmit_salt
}
self.decrypt = esp.decrypt:new{
aead = conf.aead,
spi = conf.spi,
key = conf.receive_key,
salt = conf.receive_salt,
window_size = conf.receive_window,
resync_threshold = conf.resync_threshold,
resync_attempts = conf.resync_attempts,
auditing = conf.auditing
}
self.eth = ethernet:new{
type = 0x86dd -- IPv6
}
self.ip = ipv6:new{
src = ipv6:pton(conf.self_ip),
dst = ipv6:pton(conf.nexthop_ip),
next_header = esp.PROTOCOL,
hop_limit = 64
}
return setmetatable(self, {__index = Tunnel6})
end

function Tunnel6:push ()
-- Encapsulation path
local input = self.input.decapsulated
local output = self.output.encapsulated
while not link.empty(input) do
local p = link.receive(input)
if p.length >= ethernet:sizeof() then
-- Strip Ethernet header
p = packet.shiftleft(p, ethernet:sizeof())
-- Encrypt payload
local p_enc = self.encrypt:encapsulate_tunnel(p, self.NextHeaderIPv6)
-- Slap on IPv6 and Ethernet headers
self.ip:payload_length(p_enc.length)
p_enc = packet.prepend(p_enc, self.ip:header(), ipv6:sizeof())
p_enc = packet.prepend(p_enc, self.eth:header(), ethernet:sizeof())
link.transmit(output, p_enc)
else
packet.free(p)
counter.add(self.shm.txerrors)
end
end
-- Decapsulation path
local input = self.input.encapsulated
local output = self.output.decapsulated
while not link.empty(input) do
local p = link.receive(input)
if p.length >= ethernet:sizeof() + ipv6:sizeof() then
-- Strip Ethernet and IPv6 headers
p = packet.shiftleft(p, ethernet:sizeof() + ipv6:sizeof())
-- Decrypt payload
local p_dec, nh = self.decrypt:decapsulate_tunnel(p)
if p_dec and nh == self.NextHeaderIPv6 then
-- Slap on new Ethernet header
p_dec = packet.prepend(p_dec, self.eth:header(), ethernet:sizeof())
link.transmit(output, p_dec)
goto next
end
end
-- Handle error
packet.free(p)
counter.add(self.shm.rxerrors)
::next::
end
end

function selftest ()
-- Only testing Tunnel6 because Transport6 is mostly covered in the selftest
-- of lib.ipsec.esp.
local basic_apps = require("apps.basic.basic_apps")
local c = config.new()
config.app(c, "source", basic_apps.Source)
config.app(c, "sink", basic_apps.Sink)
config.app(c, "tunnel", Tunnel6, {
self_ip = "fc00::1",
nexthop_ip = "fc00::2",
spi = 0xdeadbeef,
transmit_key = "00112233445566778899AABBCCDDEEFF",
transmit_salt = "00112233",
receive_key = "00112233445566778899AABBCCDDEEFF",
receive_salt = "00112233",
auditing = true,
selftest = true
})
config.link(c, "source.output -> tunnel.decapsulated")
config.link(c, "tunnel.encapsulated -> tunnel.encapsulated")
config.link(c, "tunnel.decapsulated -> sink.input")
engine.configure(c)
engine.main{duration=0.0001}
engine.report_links()
assert(counter.read(engine.app_table.tunnel.shm.rxerrors) == 0,
"Decapsulation error!")
print("OK")
end
85 changes: 10 additions & 75 deletions src/apps/ipsec/selftest.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#!/usr/bin/env bash
#set -x
set -e

SKIPPED_CODE=43

# Requires test_env with Linux guest featuring ipsec/ESN support.
if [ -z "$SNABB_IPSEC_ENABLE_E2E_TEST" ]; then
exit $SKIPPED_CODE
fi
Expand All @@ -12,77 +13,11 @@ if [ -z "$SNABB_TELNET0" ]; then
echo "Defaulting to SNABB_TELNET0=$SNABB_TELNET0"
fi

SPI=2953575118

SRC=fc00:feed:face:dead::1
DST=fc00:feed:face:dead::2

IP=$DST
MAC=52:54:01:00:00:

TKEY=d4d61fec2861b3b806d0654eeea02ede
TSALT=df3ddb99
TKS=$TKEY$TSALT

RKEY=c4d61fec2861b3b806d0654eeea02ede
RSALT=cf3ddb99
RKS=$RKEY$RSALT

SPORT=60122
DPORT=60123

if ! source program/snabbnfv/test_env/test_env.sh; then
echo "Could not load test_env."; exit 1
fi

./snabb snsh apps/ipsec/selftest.lua ${MAC}01 ${MAC}00 $SRC $DST $SPORT $DPORT $SPI $TKEY $TSALT $RKEY $RSALT 3 ping &
snabb_pid=$!

if ! qemu soft esp.sock $SNABB_TELNET0; then
echo "Could not start qemu."; exit $SKIPPED_CODE
fi

wait_vm_up $SNABB_TELNET0
run_telnet $SNABB_TELNET0 "systemctl stop dhcpcd.service &>/dev/console" >/dev/null
run_telnet $SNABB_TELNET0 "ifconfig eth0 down &>/dev/console" >/dev/null
run_telnet $SNABB_TELNET0 "ip -6 neigh flush dev eth0 &>/dev/console" >/dev/null
run_telnet $SNABB_TELNET0 "ip -6 neigh add $SRC lladdr ${MAC}01 dev eth0 &>/dev/console" >/dev/null
run_telnet $SNABB_TELNET0 "ip -6 addr add $DST/7 dev eth0 &>/dev/console" >/dev/null
run_telnet $SNABB_TELNET0 "ifconfig eth0 up &>/dev/console" >/dev/null
run_telnet $SNABB_TELNET0 "ncat -lkuc cat $DPORT &>/dev/console &" >/dev/null


#---------------------------------------------------------

# |-------------key--------------||-SALT-|
#KEY=0x`dd if=/dev/urandom count=32 bs=1 2> /dev/null| xxd -p -c 64`
SPI_ID=0xb00bface
#SPI_ID=0x`dd if=/dev/urandom count=4 bs=1 2> /dev/null| xxd -p -c 8`

SPISTR="spi $SPI_ID"
PROTO="proto esp"

SD_FWD="src $SRC dst $DST"
SD_REV="src $DST dst $SRC"

SDP_FWD="$SD_FWD $PROTO"
SDP_REV="$SD_REV $PROTO"

ID_FWD="$SDP_FWD $SPISTR"
ID_REV="$SDP_REV $SPISTR"

MODE="mode transport"
REPLAY="replay-window 128"
FLAG="flag esn"
RALGO="aead rfc4106\(gcm\(aes\)\) 0x$RKS 96"
TALGO="aead rfc4106\(gcm\(aes\)\) 0x$TKS 96"

cmd="echo 'spdflush; flush;' | setkey -c"
cmd="$cmd; ip xfrm state add $ID_FWD $MODE $REPLAY $FLAG $TALGO"
cmd="$cmd; ip xfrm state add $ID_REV $MODE $REPLAY $FLAG $RALGO "
cmd="$cmd; ip xfrm policy add $SD_REV dir out tmpl $SDP_REV $MODE"
cmd="$cmd; ip xfrm policy add $SD_FWD dir in tmpl $SDP_FWD $MODE"
run_telnet $SNABB_TELNET0 "$cmd &>/dev/console" >/dev/null
#---------------------------------------------------------

wait $snabb_pid
echo "Probing a Linux guest through ESP in transport mode..."
apps/ipsec/test-linux-compat.sh transport aes-gcm-16-icv
echo "Probing a Linux guest through ESP in tunnel mode..."
apps/ipsec/test-linux-compat.sh tunnel aes-gcm-16-icv
echo "Probing a Linux guest through ESP in transport mode (AES 256)..."
apps/ipsec/test-linux-compat.sh transport aes-256-gcm-16-icv
echo "Probing a Linux guest through ESP in tunnel mode (AES 256)..."
apps/ipsec/test-linux-compat.sh tunnel aes-256-gcm-16-icv
Loading