mirror of
https://github.com/XeonSquared/OC-Copper.git
synced 2024-11-27 03:38:05 +11:00
Clean up and add anti-OOM safety to culib, split data encode/decode to cdlib
This commit is contained in:
parent
880a4dff08
commit
a998d3412c
41
cdlib.lua
Normal file
41
cdlib.lua
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
-- I, 20kdc, release this into the public domain.
|
||||||
|
-- No warranty is provided, implied or otherwise.
|
||||||
|
|
||||||
|
-- 'Copper' networking implementation - encode/decode
|
||||||
|
-- Notably, it's fine that culib relies on the hops byte being the first byte,
|
||||||
|
-- because culib is supposed to know the protocol anyway,
|
||||||
|
-- and thus can rely on things like that.
|
||||||
|
-- YOU, on the other hand, cannot -
|
||||||
|
-- relib doesn't know or care about protocol internals, for example,
|
||||||
|
-- short of some "convenient" nudging of relib header size and maximum Copper data packet size.
|
||||||
|
|
||||||
|
local function encodeName(name)
|
||||||
|
if name:len() > 256 then error("Bad name (l>256)") end
|
||||||
|
if name == "" then error("No name") end
|
||||||
|
return string.char(name:len() - 1) .. name
|
||||||
|
end
|
||||||
|
local function decodeName(message)
|
||||||
|
if message:len() < 2 then return end
|
||||||
|
local nlen = message:byte(1) + 1
|
||||||
|
local fnam = message:sub(2, nlen + 1)
|
||||||
|
return fnam, message:sub(nlen + 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function decodeNoHops(data)
|
||||||
|
local src, data = decodeName(data)
|
||||||
|
if not src then return end
|
||||||
|
local dst, data = decodeName(data)
|
||||||
|
if not dst then return end
|
||||||
|
return src, dst, data
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
encode = function (hops, src, dst, data)
|
||||||
|
return string.char(hops) .. encodeName(src) .. encodeName(dst) .. data
|
||||||
|
end,
|
||||||
|
decode = function (d)
|
||||||
|
if d:len() < 3 then return end
|
||||||
|
return d:byte(1), decodeNoHops(d:sub(2))
|
||||||
|
end,
|
||||||
|
decodeNoHops = decodeNoHops
|
||||||
|
}
|
76
culib.lua
76
culib.lua
@ -1,15 +1,19 @@
|
|||||||
-- I, 20kdc, release this into the public domain.
|
-- I, 20kdc, release this into the public domain.
|
||||||
-- No warranty is provided, implied or otherwise.
|
-- No warranty is provided, implied or otherwise.
|
||||||
|
|
||||||
-- 'Copper' networking test implementation.
|
-- 'Copper' networking - "The Routing Library".
|
||||||
-- This is meant as a portable (even into OC) library for networking.
|
-- This is meant as a portable (even into OC) library for networking.
|
||||||
-- This 'outer function' is the instantiator.
|
-- This 'outer function' is the instantiator.
|
||||||
|
|
||||||
-- Note that it is probably possible to cause this code to run out of
|
-- Note that it is probably possible to cause this code to run out of
|
||||||
-- memory in several hilarious ways.
|
-- memory in several hilarious ways.
|
||||||
|
-- I've taken the approach that reduction of code .
|
||||||
|
|
||||||
-- Interfaces have no meaning, since addresses are names.
|
-- Interfaces have no meaning, since addresses are names.
|
||||||
-- Which "side" a system is on is irrelevant.
|
-- Which "side" a system is on is irrelevant.
|
||||||
|
-- (Unless you're developing a hierarchial gateway, in which case this library isn't for you
|
||||||
|
-- as it follows a default set of routing rules. Switch to cdlib for fine-grained control.)
|
||||||
|
|
||||||
-- For sending, the following function is used:
|
-- For sending, the following function is used:
|
||||||
-- transmit(nodeId, message)
|
-- transmit(nodeId, message)
|
||||||
-- The nodeId is a string or number that has been given via culib.input,
|
-- The nodeId is a string or number that has been given via culib.input,
|
||||||
@ -25,12 +29,21 @@
|
|||||||
-- "time" is a function which returns the real time, in seconds.
|
-- "time" is a function which returns the real time, in seconds.
|
||||||
-- It need not be precise.
|
-- It need not be precise.
|
||||||
-- (This is used for caches.)
|
-- (This is used for caches.)
|
||||||
|
|
||||||
|
local cdlib = require("cdlib")
|
||||||
|
|
||||||
return function (hostname, transmit, onReceive, time)
|
return function (hostname, transmit, onReceive, time)
|
||||||
|
|
||||||
-- How many packets need to be stored in seenBefore's keyspace
|
-- How many packets need to be stored in seenBefore's keyspace
|
||||||
-- before 'panic' is the best response?
|
-- before 'panic' is the best response?
|
||||||
local tuningMaxSeenBeforeCountBeforeEmergencyFlush = 0x300
|
local tuningMaxSeenBeforeCountBeforeEmergencyFlush = 0x300
|
||||||
|
|
||||||
|
-- Prevents OOM by LKR cache flooding - how many entries can the LKR have, max?
|
||||||
|
-- (Though spamming packets from many sources is now a viable method for dropping LKR,
|
||||||
|
-- it used to be a viable OOM method.)
|
||||||
|
-- Note that setting this to 0 or less will effectively result in a value of 1.
|
||||||
|
local tuningMaxLKREntries = 0x400
|
||||||
|
|
||||||
-- Expect another packet after this amount of time,
|
-- Expect another packet after this amount of time,
|
||||||
-- or else clear the known receivers cache entry.
|
-- or else clear the known receivers cache entry.
|
||||||
-- Minimum should be less or equal to tuningAttempts *
|
-- Minimum should be less or equal to tuningAttempts *
|
||||||
@ -59,12 +72,8 @@ return function (hostname, transmit, onReceive, time)
|
|||||||
-- expiry
|
-- expiry
|
||||||
-- }
|
-- }
|
||||||
local lastKnownReceiver = {}
|
local lastKnownReceiver = {}
|
||||||
|
-- How many LKR entries are there?
|
||||||
local function encodeName(name)
|
local lkrCacheCount = 0
|
||||||
if name:len() > 256 then error("Bad name (l>256)") end
|
|
||||||
if name == "" then error("No name") end
|
|
||||||
return string.char(name:len() - 1) .. name
|
|
||||||
end
|
|
||||||
|
|
||||||
local function refresh()
|
local function refresh()
|
||||||
local t = time()
|
local t = time()
|
||||||
@ -88,6 +97,21 @@ return function (hostname, transmit, onReceive, time)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Used to clean up LKR entries to prevent OOM.
|
||||||
|
local function removeOldestLKR()
|
||||||
|
local lowest = nil
|
||||||
|
local lowestExpiry = math.huge
|
||||||
|
for k, v in pairs(lastKnownReceiver) do
|
||||||
|
if v[2] < lowestExpiry then
|
||||||
|
lowest = k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if lowest then
|
||||||
|
lastKnownReceiver[lowest] = nil
|
||||||
|
lkrCacheCount = lkrCacheCount - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local culib = {}
|
local culib = {}
|
||||||
|
|
||||||
-- Can be changed.
|
-- Can be changed.
|
||||||
@ -116,52 +140,56 @@ return function (hostname, transmit, onReceive, time)
|
|||||||
seenBefore = {}
|
seenBefore = {}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Begin parsing.
|
-- Begin parsing.
|
||||||
|
|
||||||
local rawmessage = message
|
local fnam, tnam, data = cdlib.decodeNoHops(message)
|
||||||
|
if not data then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if message:len() < 2 then return end
|
if data:len() > tuningAutorejectLen then
|
||||||
local nlen = message:byte(1) + 1
|
|
||||||
local fnam = message:sub(2, nlen + 1)
|
|
||||||
message = message:sub(nlen + 2)
|
|
||||||
|
|
||||||
if message:len() < 2 then return end
|
|
||||||
local nlen = message:byte(1) + 1
|
|
||||||
local tnam = message:sub(2, nlen + 1)
|
|
||||||
message = message:sub(nlen + 2)
|
|
||||||
|
|
||||||
if message:len() > tuningAutorejectLen then
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if fnam ~= "*" then
|
if fnam ~= "*" then
|
||||||
|
if not lastKnownReceiver[fnam] then
|
||||||
|
-- if, not while, because if someone ignores my note above
|
||||||
|
-- and sets the tuning to 0 it would crash otherwise. *sigh*
|
||||||
|
if lkrCacheCount >= tuningMaxLKREntries then
|
||||||
|
removeOldestLKR()
|
||||||
|
end
|
||||||
|
lkrCacheCount = lkrCacheCount + 1
|
||||||
|
end
|
||||||
lastKnownReceiver[fnam] = {node, t + tuningExpectContinue}
|
lastKnownReceiver[fnam] = {node, t + tuningExpectContinue}
|
||||||
end
|
end
|
||||||
|
|
||||||
onReceive(fnam, tnam, message)
|
onReceive(fnam, tnam, data)
|
||||||
if culib.hostname == tnam then return end
|
if culib.hostname == tnam then return end
|
||||||
|
|
||||||
-- Redistribution of messages not aimed here
|
-- Redistribution of messages not aimed here
|
||||||
if hops == 255 then
|
if hops == 255 then
|
||||||
|
-- Don't redistribute
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
rawmessage = string.char(hops + 1) .. rawmessage
|
-- Prepend the hops byte that got removed earlier
|
||||||
|
message = string.char(hops + 1) .. message
|
||||||
end
|
end
|
||||||
|
|
||||||
local lkr = lastKnownReceiver[tnam]
|
local lkr = lastKnownReceiver[tnam]
|
||||||
if lkr then
|
if lkr then
|
||||||
culib.lkrCacheHits = culib.lkrCacheHits + 1
|
culib.lkrCacheHits = culib.lkrCacheHits + 1
|
||||||
transmit(lkr[1], rawmessage)
|
transmit(lkr[1], message)
|
||||||
else
|
else
|
||||||
culib.lkrCacheMisses = culib.lkrCacheMisses + 1
|
culib.lkrCacheMisses = culib.lkrCacheMisses + 1
|
||||||
transmit(nil, rawmessage)
|
transmit(nil, message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
culib.refresh = refresh
|
culib.refresh = refresh
|
||||||
culib.output = function (fnam, tnam, message)
|
culib.output = function (fnam, tnam, message)
|
||||||
onReceive(fnam, tnam, message)
|
onReceive(fnam, tnam, message)
|
||||||
if tnam == culib.hostname then return end
|
if tnam == culib.hostname then return end
|
||||||
local m = "\x00" .. encodeName(fnam) .. encodeName(tnam) .. message
|
local m = cdlib.encode(0, fnam, tnam, message)
|
||||||
transmit(nil, m)
|
transmit(nil, m)
|
||||||
end
|
end
|
||||||
return culib
|
return culib
|
||||||
|
Loading…
Reference in New Issue
Block a user