diff --git a/src/program/vita/README b/src/program/vita/README index 3b9ab4427b..cc3767c932 100644 --- a/src/program/vita/README +++ b/src/program/vita/README @@ -25,9 +25,9 @@ also requires a CPU that support AES-NI and AVX-2. It is also required to boot Linux with the kernel parameter iommu=off for Snabb device drivers to function. Vita will fork into five processes which individually handle key management, -outbound routing, inbound routing, encapsulation, and decapsulation. The -resulting processes can be bound to specific CPUs using the “--cpu” option in -the given order. +routing on the private port, routing on the public port, encapsulation, and +decapsulation. The resulting processes can be bound to specific CPU cores using +the “--cpu” option. Example: vita --name my-vita --cpus 0,1,2,3,4 diff --git a/src/program/vita/README.exchange b/src/program/vita/README.exchange index 22300394d2..c00f749b55 100644 --- a/src/program/vita/README.exchange +++ b/src/program/vita/README.exchange @@ -1,4 +1,4 @@ -VITA: SIMPLE KEY EXCHANGE (vita-ske, version 1g) +VITA: SIMPLE KEY EXCHANGE (vita-ske, version 1i) A simple key negotiation protocol based on pre-shared symmetric keys that provides authentication, perfect forward secrecy, and is immune to replay @@ -23,35 +23,36 @@ Notational Conventions: Description: - Let k be a pre-shared symmetric key. Let spi be a “Security Parameter Index” + Let k be a pre-shared symmetric key. Let r be a “Security Parameter Index” (SPI). Let n1 and n2 be random 256‑bit nonces, where n1 is chosen by us. ← n1 → n2 Let (p1, s1) and (p2, s2) be random, ephemeral, asymmetric key pairs, where - (p1, s1) is choosen by us. Let h1 = HMAC(k, spi ‖ n1 ‖ n2 ‖ p1). + (p1, s1) is choosen by us. Let spi1 and spi2 be child SPIs, where spi1 is + choosen by us. Let h1 = HMAC(k, r ‖ n1 ‖ n2 ‖ spi1 ‖ p1). - ← p1 ‖ h1 - → p2 ‖ h2 + ← spi1 ‖ p1 ‖ h1 + → spi2 ‖ p2 ‖ h2 - Ensure that h2 = HMAC(k, spi ‖ n2 ‖ n1 ‖ p2). Let q = DH(s1, p2), and ensure - that p2 is a valid argument. Let rx = HASH(q ‖ p1 ‖ p2) and - tx = HASH(q ‖ p2 ‖ p1) be key material. Assign (spi, rx) to the incoming - “Security Association” (SA), and (spi, tx) to the outgoing SA. + Ensure that h2 = HMAC(k, r ‖ n2 ‖ n1 ‖ spi2 ‖ p2). Let q = DH(s1, p2), and + ensure that p2 is a valid argument. Let rx = HASH(q ‖ p1 ‖ p2) and + tx = HASH(q ‖ p2 ‖ p1) be key material. Assign (spi1, rx) to the inbound + “Security Association” (SA), and (spi2, tx) to the outbound SA. The description above illustrates the perspective of an active party adhering to the protocol, i.e. the exchange is initiated by us. An opposite, passive party adhering to the protocol, i.e. one that is merely responding to a key exchange initiated by an active party, must ensure that the tuple - (spi, p2, h2) was received and authenticated before computing and sending its - response tuple (spi, p1, h1). For a passive party the order of messages during - the exchange is reversed: + (spi2, p2, h2) was received and authenticated before computing and sending its + response tuple (spi1, p1, h1). For a passive party the order of messages + during the exchange is reversed: → n2 ← n1 - → p2 ‖ h2 (ensure h2 is verified before we reply) - ← p1 ‖ h1 + → spi2 ‖ p2 ‖ h2 (ensure h2 is verified before we reply) + ← spi1 ‖ p1 ‖ h1 Note that there might be no passive party in an exchange if both parties have initiated the exchange before receiving an initial nonce message. @@ -60,8 +61,8 @@ Security Proof: Assuming an attacker can not guess k, and n1 has enough entropy so that the probability that the tuple (n1, p2) has occurred before is negligible, they - are unable to produce the value h2 = HMAC(k, spi ‖ n2 ‖ n1 ‖ p2), and thus are - unable to complete a key exchange. + are unable to produce the value h2 = HMAC(k, r ‖ n2 ‖ n1 ‖ spi2 ‖ p2), and + thus are unable to complete a key exchange. Assuming an attacker can not guess s1 or s2, they are unable to produce q = DH(s1, p2) or q = DH(s2, p1), and subsequently derive rx or tx, and thus @@ -73,6 +74,9 @@ Security Proof: Notes: + • The outputs of this protocol are choosen specifically to support the AES-GCM + algorithm with 128‑bit keys as used in an IPsec ESP data plane. + • Future versions of this protocol may introduce the use of a key derivation function (KDF), such as libsodium’s crypto_kdf_blake2b_derive_from_key, to derive additional key material from rx or tx. @@ -81,9 +85,15 @@ References: • The spiped protocol: https://github.com/Tarsnap/spiped/blob/d1e62a8/DESIGN.md#spiped-design + • Additional discussion of the spiped protocol: https://github.com/Tarsnap/spiped/issues/151 + • The Sodium crypto library (libsodium): https://download.libsodium.org/doc/ + • Security Architecture for the Internet Protocol: https://tools.ietf.org/html/rfc4301 + + • The use of AES-GCM in IPsec ESP: + https://tools.ietf.org/html/rfc4106 diff --git a/src/program/vita/exchange.lua b/src/program/vita/exchange.lua index 1998afa02b..29e0acf8a5 100644 --- a/src/program/vita/exchange.lua +++ b/src/program/vita/exchange.lua @@ -15,7 +15,6 @@ module(...,package.seeall) -- --> KeyManager *--> esp_worker -- | -- \--> dsp_worker - -- -- All things considered, this is the hairy part of Vita, as it covers touchy -- things such as key generation and expiration, and ultimately presents Vita’s @@ -41,7 +40,7 @@ module(...,package.seeall) -- lifetime of a SA pair has expired (sa_ttl), it is destroyed, and -- eventually re-negotiated if applicable. -- --- Note that the KeyManager app will attempts to re-negotiate SAs long +-- Note that the KeyManager app will attempt to re-negotiate SAs long -- before they expire (specifically, once half of sa_ttl has passed), in -- order to avoid loss of tunnel connectivity during re-negotiation. -- @@ -224,8 +223,8 @@ function KeyManager:reconfig (conf) preshared_key = new_key, spi = route.spi, status = status.expired, - rx_sa = nil, tx_sa = nil, - sa_timeout = nil, rekey_timeout = nil, + rx_sa = nil, prev_rx_sa = nil, tx_sa = nil, + sa_timeout = nil, prev_sa_timeout = nil, rekey_timeout = nil, protocol = Protocol:new(route.spi, new_key, conf.negotiation_ttl) } table.insert(new_routes, new_route) @@ -262,14 +261,18 @@ function KeyManager:push () counter.add(self.shm.negotiations_expired) audit:log("Negotiation expired for '"..route.id.."' (negotiation_ttl)") end - if route.status < status.ready then - self:negotiate(route) - elseif route.rekey_timeout() then - route.status = status.rekey - elseif route.sa_timeout() then + if route.status > status.expired and route.sa_timeout() then counter.add(self.shm.keypairs_expired) audit:log("Keys expired for '"..route.id.."' (sa_ttl)") self:expire_route(route) + elseif route.prev_sa_timeout and route.prev_sa_timeout() then + self:expire_prev_sa(route) + end + if route.status > status.rekey and route.rekey_timeout() then + route.status = status.rekey + end + if route.status < status.ready then + self:negotiate(route) end end end @@ -336,7 +339,8 @@ function KeyManager:handle_key_request (route, message) else assert(not ecode) end counter.add(self.shm.keypairs_negotiated) - audit:log("Completed key exchange for '"..route.id.."'") + audit:log(("Completed key exchange for '%s' (rx-spi %d, tx-spi %d)"): + format(route.id, rx.spi, tx.spi)) if response then link.transmit(self.output.output, self:request(route, response)) @@ -348,15 +352,25 @@ function KeyManager:handle_key_request (route, message) end function KeyManager:configure_route (route, rx, tx) + for _, route in ipairs(self.routes) do + if (route.rx_sa and route.rx_sa.spi == rx.spi) + or (route.prev_rx_sa and route.prev_rx_sa.spi == rx.spi) then + error("PANIC: SPI collision detected.") + end + end route.status = status.ready + route.prev_rx_sa = route.rx_sa + route.prev_sa_timeout = route.sa_timeout route.rx_sa = { route = route.id, + spi = rx.spi, aead = "aes-gcm-16-icv", key = lib.hexdump(rx.key), salt = lib.hexdump(rx.salt) } route.tx_sa = { route = route.id, + spi = tx.spi, aead = "aes-gcm-16-icv", key = lib.hexdump(tx.key), salt = lib.hexdump(tx.salt) @@ -370,11 +384,19 @@ function KeyManager:expire_route (route) route.status = status.expired route.tx_sa = nil route.rx_sa = nil + route.prev_rx_sa = nil + route.prev_sa_timeout = nil route.sa_timeout = nil route.rekey_timeout = nil self:commit_sa_db() end +function KeyManager:expire_prev_sa (route) + route.prev_rx_sa = nil + route.prev_sa_timeout = nil + self:commit_sa_db() +end + function KeyManager:request (route, message) local request = packet.allocate() @@ -443,8 +465,11 @@ function KeyManager:commit_sa_db () local esp_keys, dsp_keys = {}, {} for _, route in ipairs(self.routes) do if route.status == status.ready then - esp_keys[route.spi] = route.tx_sa - dsp_keys[route.spi] = route.rx_sa + esp_keys[route.tx_sa.spi] = route.tx_sa + dsp_keys[route.rx_sa.spi] = route.rx_sa + if route.prev_rx_sa then + dsp_keys[route.prev_rx_sa.spi] = route.prev_rx_sa + end end end -- Commit active SAs to SA database @@ -458,7 +483,7 @@ function KeyManager:commit_sa_db () db:close() end --- Vita: simple key exchange (vita-ske, version 1g). See README.exchange +-- Vita: simple key exchange (vita-ske, version 1i). See README.exchange -- -- NB: this implementation introduces two pseudo states _send_key and _complete -- not present in fsm-protocol.svg. The _send_key state is inserted in between @@ -472,6 +497,7 @@ Protocol = { status = { idle = 0, wait_nonce = 1, wait_key = 2, _send_key = -1, _complete = -2 }, code = { protocol = 0, authentication = 1, parameter = 2, expired = 3}, + spi_counter = 0, preshared_key_bytes = C.crypto_auth_hmacsha512256_KEYBYTES, public_key_bytes = C.crypto_scalarmult_curve25519_BYTES, secret_key_bytes = C.crypto_scalarmult_curve25519_SCALARBYTES, @@ -501,6 +527,7 @@ Protocol.nonce_message:init({ Protocol.key_message:init({ [1] = ffi.typeof([[ struct { + uint8_t spi[]]..ffi.sizeof(Protocol.spi_t)..[[]; uint8_t public_key[]]..Protocol.public_key_bytes..[[]; uint8_t auth_code[]]..Protocol.auth_code_bytes..[[]; } __attribute__((packed)) @@ -525,11 +552,20 @@ end function Protocol.key_message:new (config) local o = Protocol.key_message:superClass().new(self) + o:spi(config.spi) o:public_key(config.public_key) o:auth_code(config.auth_code) return o end +function Protocol.key_message:spi (spi) + local h = self:header() + if spi ~= nil then + ffi.copy(h.spi, spi, ffi.sizeof(h.spi)) + end + return h.spi +end + function Protocol.key_message:public_key (public_key) local h = self:header() if public_key ~= nil then @@ -546,15 +582,17 @@ function Protocol.key_message:auth_code (auth_code) return h.auth_code end -function Protocol:new (spi, key, timeout) +function Protocol:new (r, key, timeout) local o = { status = Protocol.status.idle, timeout = timeout, deadline = nil, k = ffi.new(Protocol.buffer_t, Protocol.preshared_key_bytes), - spi = ffi.new(Protocol.spi_t), + r = ffi.new(Protocol.spi_t), n1 = ffi.new(Protocol.buffer_t, Protocol.nonce_bytes), n2 = ffi.new(Protocol.buffer_t, Protocol.nonce_bytes), + spi1 = ffi.new(Protocol.spi_t), + spi2 = ffi.new(Protocol.spi_t), s1 = ffi.new(Protocol.buffer_t, Protocol.secret_key_bytes), p1 = ffi.new(Protocol.buffer_t, Protocol.public_key_bytes), p2 = ffi.new(Protocol.buffer_t, Protocol.public_key_bytes), @@ -565,7 +603,7 @@ function Protocol:new (spi, key, timeout) hash_state = ffi.new("struct crypto_generichash_blake2b_state") } ffi.copy(o.k, key, ffi.sizeof(o.k)) - o.spi.u32 = lib.htonl(spi) + o.r.u32 = lib.htonl(r) return setmetatable(o, {__index=Protocol}) end @@ -612,8 +650,8 @@ function Protocol:derive_ephemeral_keys () if self.status == Protocol.status._complete then self:reset() if self:derive_shared_secret() then - local rx = self:derive_key_material(self.p1, self.p2) - local tx = self:derive_key_material(self.p2, self.p1) + local rx = self:derive_key_material(self.spi1, self.p1, self.p2) + local tx = self:derive_key_material(self.spi2, self.p2, self.p1) return nil, rx, tx else return Protocol.code.paramter end else return Protocol.code.protocol end @@ -638,30 +676,35 @@ function Protocol:intern_nonce (nonce_message) end function Protocol:send_key (key_message) - local spi, k, n1, n2, s1, p1 = - self.spi, self.k, self.n1, self.n2, self.s1, self.p1 + local r, k, n1, n2, spi1, s1, p1 = + self.r, self.k, self.n1, self.n2, self.spi1, self.s1, self.p1 local state, h1 = self.hmac_state, self.h + spi1.u32 = lib.htonl(Protocol:next_spi()) C.randombytes_buf(s1, ffi.sizeof(s1)) C.crypto_scalarmult_curve25519_base(p1, s1) C.crypto_auth_hmacsha512256_init(state, k, ffi.sizeof(k)) - C.crypto_auth_hmacsha512256_update(state, spi.bytes, ffi.sizeof(spi)) + C.crypto_auth_hmacsha512256_update(state, r.bytes, ffi.sizeof(r)) C.crypto_auth_hmacsha512256_update(state, n1, ffi.sizeof(n1)) C.crypto_auth_hmacsha512256_update(state, n2, ffi.sizeof(n2)) + C.crypto_auth_hmacsha512256_update(state, spi1.bytes, ffi.sizeof(spi1)) C.crypto_auth_hmacsha512256_update(state, p1, ffi.sizeof(p1)) C.crypto_auth_hmacsha512256_final(state, h1) - return key_message:new({public_key=p1, auth_code=h1}) + return key_message:new({spi = spi1.bytes, public_key=p1, auth_code=h1}) end function Protocol:intern_key (m) - local spi, k, n1, n2, p2 = self.spi, self.k, self.n1, self.n2, self.p2 + local r, k, n1, n2, spi2, p2 = + self.r, self.k, self.n1, self.n2, self.spi2, self.p2 local state, h2 = self.hmac_state, self.h C.crypto_auth_hmacsha512256_init(state, k, ffi.sizeof(k)) - C.crypto_auth_hmacsha512256_update(state, spi.bytes, ffi.sizeof(spi)) + C.crypto_auth_hmacsha512256_update(state, r.bytes, ffi.sizeof(r)) C.crypto_auth_hmacsha512256_update(state, n2, ffi.sizeof(n2)) C.crypto_auth_hmacsha512256_update(state, n1, ffi.sizeof(n1)) + C.crypto_auth_hmacsha512256_update(state, m:spi(), ffi.sizeof(spi2)) C.crypto_auth_hmacsha512256_update(state, m:public_key(), ffi.sizeof(p2)) C.crypto_auth_hmacsha512256_final(state, h2) if C.sodium_memcmp(h2, m:auth_code(), ffi.sizeof(h2)) == 0 then + ffi.copy(spi2.bytes, m:spi(), ffi.sizeof(spi2)) ffi.copy(p2, m:public_key(), ffi.sizeof(p2)) return true end @@ -671,14 +714,15 @@ function Protocol:derive_shared_secret () return C.crypto_scalarmult_curve25519(self.q, self.s1, self.p2) == 0 end -function Protocol:derive_key_material (salt_a, salt_b) +function Protocol:derive_key_material (spi, salt_a, salt_b) local q, e, state = self.q, self.e, self.hash_state C.crypto_generichash_blake2b_init(state, nil, 0, ffi.sizeof(e)) C.crypto_generichash_blake2b_update(state, q, ffi.sizeof(q)) C.crypto_generichash_blake2b_update(state, salt_a, ffi.sizeof(salt_a)) C.crypto_generichash_blake2b_update(state, salt_b, ffi.sizeof(salt_b)) C.crypto_generichash_blake2b_final(state, e.bytes, ffi.sizeof(e.bytes)) - return { key = ffi.string(e.slot.key, ffi.sizeof(e.slot.key)), + return { spi = lib.ntohl(spi.u32), + key = ffi.string(e.slot.key, ffi.sizeof(e.slot.key)), salt = ffi.string(e.slot.salt, ffi.sizeof(e.slot.salt)) } end @@ -691,11 +735,19 @@ function Protocol:set_deadline () self.deadline = lib.timeout(self.timeout) end +function Protocol:next_spi () + local current_spi = Protocol.spi_counter + 256 + Protocol.spi_counter = (Protocol.spi_counter + 1) % (2^32 - 1 - 256) + return current_spi +end + -- Assertions about the world (-: assert(Protocol.preshared_key_bytes == 32) assert(Protocol.public_key_bytes == 32) assert(Protocol.auth_code_bytes == 32) +assert(ffi.sizeof(Protocol.key_t) >= C.crypto_generichash_blake2b_BYTES_MIN) +assert(ffi.sizeof(Protocol.key_t) <= C.crypto_generichash_blake2b_BYTES_MAX) -- Transport wrapper for vita-ske that encompasses an SPI to map requests to -- routes, and a message type to facilitate parsing. @@ -704,7 +756,7 @@ assert(Protocol.auth_code_bytes == 32) -- requests through protocol filters. Transport = { - message_type = { nonce = 1, key = 2 }, + message_type = { nonce = 1, key = 3 }, header = subClass(header) } Transport.header:init({ diff --git a/src/program/vita/route.lua b/src/program/vita/route.lua index 723b48c08c..82d1194eca 100644 --- a/src/program/vita/route.lua +++ b/src/program/vita/route.lua @@ -101,7 +101,7 @@ end PublicRouter = { name = "PublicRouter", config = { - routes = {required=true} + sa = {required=true} }, shm = { route_errors = {counter} @@ -109,23 +109,33 @@ PublicRouter = { } function PublicRouter:new (conf) - local index_t = ffi.typeof("uint32_t") local o = { - ports = {}, - routes = {}, routing_table4 = ctable.new{ - key_type = index_t, - value_type = index_t + key_type = ffi.typeof("uint32_t"), + value_type = ffi.typeof("uint32_t") }, esp = esp:new({}) } - for id, route in pairs(conf.routes) do - local index = #o.ports+1 - assert(ffi.cast(index_t, index) == index, "index overflow") - o.routing_table4:add(assert(route.spi, "Missing SPI"), index) - o.ports[index] = id + return setmetatable(o, {__index = PublicRouter}):reconfig(conf) +end + +function PublicRouter:reconfig (conf) + self.ports = {} + self.routes = {} + -- Update FIB entries for SAs + for spi, sa in pairs(conf.sa) do + local index = #self.ports+1 + assert(ffi.cast("uint32_t", index) == index, "index overflow") + self.routing_table4:add(spi, index, 'update') + self.ports[index] = sa.route.."_"..spi + end + -- Remove obsolete FIB entries + for entry in self.routing_table4:iterate() do + if not conf.sa[tonumber(entry.key)] then + self.routing_table4:remove_ptr(entry) + end end - return setmetatable(o, {__index = PublicRouter}) + return self end function PublicRouter:link () diff --git a/src/program/vita/selftest.snabb b/src/program/vita/selftest.snabb index 60e8be1f31..f0c62f7881 100755 --- a/src/program/vita/selftest.snabb +++ b/src/program/vita/selftest.snabb @@ -48,10 +48,7 @@ local cfg = { preshared_key = string.rep("00", 32), spi = 1001 } - } -} - -local sa_db = { + }, outbound_sa = { [1001] = { route = "loopback", @@ -76,8 +73,8 @@ local c = config.new() -- Configure Vita app network (all-in-one process.) local _, private = vita.configure_private_router(cfg, c) local _, public = vita.configure_public_router(cfg, c) -vita.configure_esp(sa_db, c) -vita.configure_dsp(sa_db, c) +vita.configure_esp(cfg, c) +vita.configure_dsp(cfg, c) -- Add ARP resolvers. config.app(c, "private_arp", ARP, { @@ -187,7 +184,7 @@ else rxerrors = 2, fragment_errors = 2 }, - DSP_loopback = { + DSP_loopback_1001 = { rxerrors = 2, protocol_errors = 1, decrypt_errors = 1 diff --git a/src/program/vita/sodium.h b/src/program/vita/sodium.h index 82f28b8b07..64141906b6 100644 --- a/src/program/vita/sodium.h +++ b/src/program/vita/sodium.h @@ -49,8 +49,8 @@ int crypto_scalarmult_curve25519(unsigned char *q, // crypto_generichash_blake2b.h enum { - crypto_generichash_blake2b_BYTES = 32U, - crypto_generichash_blake2b_KEYBYTES = 32U + crypto_generichash_blake2b_BYTES_MIN = 16U, + crypto_generichash_blake2b_BYTES_MAX = 64U }; struct crypto_generichash_blake2b_state { uint64_t h[8]; diff --git a/src/program/vita/test.lua b/src/program/vita/test.lua index 3770d53142..1018ab8595 100644 --- a/src/program/vita/test.lua +++ b/src/program/vita/test.lua @@ -145,15 +145,15 @@ function run_softbench (pktsize, npackets, nroutes, cpuspec) local function softbench_workers (conf) return { key_manager = vita.configure_exchange(conf), - inbound_gauge_router = configure_private_router_softbench(conf), - outbound_loopback_router = configure_public_router_loopback(conf), + private_gauge_router = configure_private_router_softbench(conf), + public_loopback_router = configure_public_router_loopback(conf), encapsulate = vita.configure_esp(conf), decapsulate = vita.configure_dsp(conf) } end local function wait_gauge () - if not worker.status().inbound_gauge_router.alive then + if not worker.status().private_gauge_router.alive then main.exit() end end diff --git a/src/program/vita/vita.lua b/src/program/vita/vita.lua index 37c2cbdb52..8981cc6ab2 100644 --- a/src/program/vita/vita.lua +++ b/src/program/vita/vita.lua @@ -223,8 +223,8 @@ end function vita_workers (conf) return { key_manager = configure_exchange(conf), - outbound_router = configure_private_router_with_nic(conf), - inbound_router = configure_public_router_with_nic(conf), + private_router = configure_private_router_with_nic(conf), + public_router = configure_public_router_with_nic(conf), encapsulate = configure_esp(conf), decapsulate = configure_dsp(conf) } @@ -281,9 +281,11 @@ function configure_private_router (conf, append) local ESP_in = "ESP_"..id.."_in" config.app(c, ESP_in.."_Tx", Transmitter, ESP_in) config.link(c, private_in.." -> "..ESP_in.."_Tx.input") + end - local private_out = "InboundDispatch."..id - local DSP_out = "DSP_"..id.."_out" + for spi, sa in pairs(conf.inbound_sa) do + local private_out = "InboundDispatch."..sa.route.."_"..spi + local DSP_out = "DSP_"..sa.route.."_"..spi.."_out" config.app(c, DSP_out.."_Rx", Receiver, DSP_out) config.link(c, DSP_out.."_Rx.output -> "..private_out) end @@ -305,7 +307,7 @@ function configure_public_router (conf, append) node_ip4 = conf.public_interface.ip4 }) config.app(c, "PublicRouter", route.PublicRouter, { - routes = conf.route + sa = conf.inbound_sa }) config.app(c, "PublicICMP4", icmp.ICMP4, { node_ip4 = conf.public_interface.ip4 @@ -328,11 +330,6 @@ function configure_public_router (conf, append) config.link(c, "Protocol_out_Rx.output -> PublicNextHop.protocol") for id, route in pairs(conf.route) do - local public_in = "PublicRouter."..id - local DSP_in = "DSP_"..id.."_in" - config.app(c, DSP_in.."_Tx", Transmitter, DSP_in) - config.link(c, public_in.." -> "..DSP_in.."_Tx.input") - local public_out = "PublicNextHop."..id local ESP_out = "ESP_"..id.."_out" local Tunnel = "Tunnel_"..id @@ -343,6 +340,13 @@ function configure_public_router (conf, append) config.link(c, Tunnel..".output -> "..public_out) end + for spi, sa in pairs(conf.inbound_sa) do + local public_in = "PublicRouter."..sa.route.."_"..spi + local DSP_in = "DSP_"..sa.route.."_"..spi.."_in" + config.app(c, DSP_in.."_Tx", Transmitter, DSP_in) + config.link(c, public_in.." -> "..DSP_in.."_Tx.input") + end + local public_links = { input = "PublicDispatch.input", output = "PublicNextHop.output" @@ -443,17 +447,18 @@ function configure_dsp (sa_db, append) for spi, sa in pairs(sa_db.inbound_sa) do -- Configure interlink receiver/transmitter for inbound SA - local DSP_in = "DSP_"..sa.route.."_in" - local DSP_out = "DSP_"..sa.route.."_out" + local DSP_in = "DSP_"..sa.route.."_"..spi.."_in" + local DSP_out = "DSP_"..sa.route.."_"..spi.."_out" config.app(c, DSP_in.."_Rx", Receiver, DSP_in) config.app(c, DSP_out.."_Tx", Transmitter, DSP_out) -- Configure inbound SA - local DSP = "DSP_"..sa.route + local DSP = "DSP_"..sa.route.."_"..spi config.app(c, DSP, tunnel.Decapsulate, { spi = spi, aead = sa.aead, key = sa.key, - salt = sa.salt + salt = sa.salt, + auditing = true }) config.link(c, DSP_in.."_Rx.output -> "..DSP..".input") config.link(c, DSP..".output4 -> "..DSP_out.."_Tx.input")