Skip to content

Commit

Permalink
Merge PR #953 (Misc NFV stats counters) into kbara-next
Browse files Browse the repository at this point in the history
  • Loading branch information
Katerina Barone-Adesi committed Jul 5, 2016
2 parents d9e52ab + d52d89c commit 51c9d51
Show file tree
Hide file tree
Showing 17 changed files with 398 additions and 9 deletions.
44 changes: 44 additions & 0 deletions src/apps/ipsec/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# IPsec Apps

## AES128gcm (apps.ipsec.esp)

The `AES128gcm` implements an ESP transport tunnel 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.

References:

- `lib.ipsec.esp`

DIAGRAM: AES128gcm
+-----------+
encapsulated | |
---->* AES128gcm *<----
<----* *---->
| | decapsulated
+-----------+

encapsulated
--------\ /----------
<-------|---/ /------->
\-----/ decapsulated

### Configuration

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

— Key **spi**

*Required*. Security Parameter Index. A 32 bit integer.

— Key **key**

*Required*. 20 bytes in form of a hex encoded string.

— Key **replay_window**

*Optional*. Size of the “Anti-Replay Window”. Defaults to 128.
70 changes: 70 additions & 0 deletions src/apps/ipsec/esp.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
-- 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
-- AES-128-GCM.

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

AES128gcm = {}

local provided_counters = {
'type', 'dtime', 'txerrors', 'rxerrors'
}

function AES128gcm:new (arg)
local conf = arg and config.parse_app_arg(arg) or {}
local self = {}
self.encrypt = esp.esp_v6_encrypt:new{
mode = "aes-128-gcm",
spi = conf.spi,
keymat = conf.key:sub(1, 32),
salt = conf.key:sub(33, 40)}
self.decrypt = esp.esp_v6_decrypt:new{
mode = "aes-128-gcm",
spi = conf.spi,
keymat = conf.key:sub(1, 32),
salt = conf.key:sub(33, 40),
window_size = conf.replay_window}
self.counters = {}
for _, name in ipairs(provided_counters) do
self.counters[name] = counter.open(name)
end
counter.set(self.counters.type, 0x1001) -- Virtual interface
counter.set(self.counters.dtime, C.get_unix_time())
return setmetatable(self, {__index = AES128gcm})
end

function AES128gcm:push ()
-- Encapsulation path
local input = self.input.decapsulated
local output = self.output.encapsulated
for _=1,link.nreadable(input) do
local p = link.receive(input)
if self.encrypt:encapsulate(p) then
link.transmit(output, p)
else
packet.free(p)
counter.add(self.counters.txerrors)
end
end
-- Decapsulation path
local input = self.input.encapsulated
local output = self.output.decapsulated
for _=1,link.nreadable(input) do
local p = link.receive(input)
if self.decrypt:decapsulate(p) then
link.transmit(output, p)
else
packet.free(p)
counter.add(self.counters.rxerrors)
end
end
end

function AES128gcm:stop ()
-- delete counters
for name, _ in pairs(self.counters) do counter.delete(name) end
end
47 changes: 47 additions & 0 deletions src/apps/ipv6/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,30 @@ milliseconds. Default is 1,000ms.
*Optional*. Number of neighbor solicitation retransmissions. Default is
unlimited retransmissions.

### Special Counters

— Key **ns_checksum_errors**

Neighbor solicitation requests dropped due to invalid ICMP checksum.

— Key **ns_target_address_errors**

Neighbor solicitation requests dropped due to invalid target address.

— Key **na_duplicate_errors**

Neighbor advertisement requests dropped because next-hop is already resolved.

— Key **na_target_address_errors**

Neighbor advertisement requests dropped due to invalid target address.

— Key **nd_protocol_errors**

Neighbor discovery requests dropped due to protocol errors (invalid IPv6
hop-limit or invalid neighbor solicitation request options).


## SimpleKeyedTunnel (apps.keyed_ipv6_tunnel.tunnel)

The `SimpleKeyedTunnel` app implements "a simple L2 Ethernet over IPv6
Expand Down Expand Up @@ -115,3 +139,26 @@ the L2TPv3 header will be overwritten with this value.

*Optional*. Destination MAC as a string. Not required if overwritten by
an app such as `nd_light`.


### Special Counters

— Key **length_errors**

Ingress packets dropped due to invalid length (packet too short).

— Key **protocol_errors**

Ingress packets dropped due to unrecognized IPv6 protocol ID.

— Key **cookie_errors**

Ingress packets dropped due to wrong cookie value.

— Key **remote_address_errors**

Ingress packets dropped due to wrong remote IPv6 endpoint address.

— Key **local_address_errors**

Ingress packets dropped due to wrong local IPv6 endpoint address.
40 changes: 38 additions & 2 deletions src/apps/ipv6/nd_light.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ local app = require("core.app")
local link = require("core.link")
local config = require("core.config")
local packet = require("core.packet")
local counter = require("core.counter")
local datagram = require("lib.protocol.datagram")
local ethernet = require("lib.protocol.ethernet")
local ipv6 = require("lib.protocol.ipv6")
Expand Down Expand Up @@ -78,6 +79,13 @@ local function check_ip_address(ip, desc)
return ip
end

local provided_counters = {
'type', 'dtime', 'status', 'rxerrors', 'txerrors', 'txdrop',
'ns_checksum_errors', 'ns_target_address_errors',
'na_duplicate_errors', 'na_target_address_errors',
'nd_protocol_errors'
}

function nd_light:new (arg)
local arg = arg and config.parse_app_arg(arg) or {}
--copy the args to avoid changing the arg table so that it stays reusable.
Expand Down Expand Up @@ -201,6 +209,16 @@ function nd_light:new (arg)
mem = ffi.new("uint8_t *[1]")
}
o._logger = lib.logger_new({ module = 'nd_light' })

-- Create counters
o.counters = {}
for _, name in ipairs(provided_counters) do
o.counters[name] = counter.open(name)
end
counter.set(o.counters.type, 0x1001) -- Virtual interface
counter.set(o.counters.dtime, C.get_unix_time())
counter.set(o.counters.status, 2) -- Link down

return o
end

Expand All @@ -209,13 +227,16 @@ local function ns (self, dgram, eth, ipv6, icmp)
local mem, length = self._cache.mem
mem[0], length = dgram:payload()
if not icmp:checksum_check(mem[0], length, ipv6) then
self._logger:log("bad icmp checksum")
counter.add(self.counters.ns_checksum_errors)
counter.add(self.counters.rxerrors)
return nil
end
-- Parse the neighbor solicitation and check if it contains our own
-- address as target
local ns = dgram:parse_match(nil, self._match_ns)
if not ns then
counter.add(self.counters.ns_target_address_errors)
counter.add(self.counters.rxerrors)
return nil
end
-- Ignore options as long as we don't implement a proper neighbor
Expand All @@ -236,22 +257,29 @@ end
-- Process neighbor advertisement
local function na (self, dgram, eth, ipv6, icmp)
if self._eth_header then
counter.add(self.counters.na_duplicate_errors)
counter.add(self.counters.rxerrors)
return nil
end
local na = dgram:parse_match(nil, self._match_na)
if not na then
counter.add(self.counters.na_target_address_errors)
counter.add(self.counters.rxerrors)
return nil
end
local option = na:options(dgram:payload())
if not (#option == 1 and option[1]:type() == 2) then
-- Invalid NS, ignore
counter.add(self.counters.nd_protocol_errors)
counter.add(self.counters.rxerrors)
return nil
end
self._eth_header = ethernet:new({ src = self._config.local_mac,
dst = option[1]:option():addr(),
type = 0x86dd })
self._logger:log(string.format("Resolved next-hop %s to %s", ipv6:ntop(self._config.next_hop),
ethernet:ntop(option[1]:option():addr())))
counter.set(self.counters.status, 1) -- Link up
return nil
end

Expand All @@ -265,6 +293,8 @@ local function from_south (self, p)
local eth, ipv6, icmp = unpack(dgram:stack())
if ipv6:hop_limit() ~= 255 then
-- Avoid off-link spoofing as per RFC
counter.add(self.counters.nd_protocol_errors)
counter.add(self.counters.rxerrors)
return nil
end
local result
Expand Down Expand Up @@ -311,13 +341,17 @@ function nd_light:push ()
-- Drop packets until ND for the next-hop
-- has completed.
packet.free(link.receive(l_in))
counter.add(self.counters.txdrop)
else
local p = cache.p
p[0] = link.receive(l_in)
if p[0].length >= self._eth_header:sizeof() then
self._eth_header:copy(p[0].data)
link.transmit(l_out, p[0])
else packet.free(p[0]) end
else
packet.free(p[0])
counter.add(self.counters.txerrors)
end
end
end
end
Expand All @@ -328,6 +362,8 @@ function nd_light:stop ()
self._next_hop.packet = nil
packet.free(self._sna.packet)
self._sna.packet = nil
-- delete counters
for name, _ in pairs(self.counters) do counter.delete(name) end
end

function selftest ()
Expand Down
28 changes: 27 additions & 1 deletion src/apps/keyed_ipv6_tunnel/tunnel.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ local link = require("core.link")
local lib = require("core.lib")
local packet = require("core.packet")
local config = require("core.config")
local counter = require("core.counter")

local macaddress = require("lib.macaddress")

Expand Down Expand Up @@ -102,6 +103,12 @@ end

SimpleKeyedTunnel = {}

local provided_counters = {
'type', 'dtime', 'rxerrors',
'length_errors', 'protocol_errors', 'cookie_errors',
'remote_address_errors', 'local_address_errors'
}

function SimpleKeyedTunnel:new (arg)
local conf = arg and config.parse_app_arg(arg) or {}
-- required fields:
Expand Down Expand Up @@ -163,12 +170,20 @@ function SimpleKeyedTunnel:new (arg)
header[HOP_LIMIT_OFFSET] = conf.hop_limit
end

local counters = {}
for _, name in ipairs(provided_counters) do
counters[name] = counter.open(name)
end
counter.set(counters.type, 0x1001) -- Virtual interface
counter.set(counters.dtime, C.get_unix_time())

local o =
{
header = header,
remote_address = remote_address,
local_address = local_address,
remote_cookie = remote_cookie[0]
remote_cookie = remote_cookie[0],
counters = counters
}

return setmetatable(o, {__index = SimpleKeyedTunnel})
Expand Down Expand Up @@ -198,36 +213,42 @@ function SimpleKeyedTunnel:push()
local drop = true
repeat
if p.length < HEADER_SIZE then
counter.add(self.counters.length_errors)
break
end
local next_header = ffi.cast(next_header_ctype, p.data + NEXT_HEADER_OFFSET)
if next_header[0] ~= L2TPV3_NEXT_HEADER then
counter.add(self.counters.protocol_errors)
break
end

local cookie = ffi.cast(pcookie_ctype, p.data + COOKIE_OFFSET)
if cookie[0] ~= self.remote_cookie then
counter.add(self.counters.cookie_errors)
break
end

local remote_address = ffi.cast(paddress_ctype, p.data + SRC_IP_OFFSET)
if remote_address[0] ~= self.remote_address[0] or
remote_address[1] ~= self.remote_address[1]
then
counter.add(self.counters.remote_address_errors)
break
end

local local_address = ffi.cast(paddress_ctype, p.data + DST_IP_OFFSET)
if local_address[0] ~= self.local_address[0] or
local_address[1] ~= self.local_address[1]
then
counter.add(self.counters.local_address_errors)
break
end

drop = false
until true

if drop then
counter.add(self.counters.rxerrors)
-- discard packet
packet.free(p)
else
Expand All @@ -237,6 +258,11 @@ function SimpleKeyedTunnel:push()
end
end

function SimpleKeyedTunnel:stop ()
-- delete counters
for name, _ in pairs(self.counters) do counter.delete(name) end
end

-- prepare header template to be used by all apps
prepare_header_template()

Expand Down
Loading

0 comments on commit 51c9d51

Please sign in to comment.