mirror of
https://github.com/XeonSquared/OC-Copper.git
synced 2024-09-21 18:58:47 +10:00
880a4dff08
The tests assume a mesh network with known connections, the node doesn't, so unneeded packet leakage isn't really as controllable as I'd hoped. What's definitely important is that the system doesn't cache entries for long enough that it starts continuously sending packets the wrong way no matter what.
169 lines
4.7 KiB
Lua
169 lines
4.7 KiB
Lua
-- I, 20kdc, release this into the public domain.
|
|
-- No warranty is provided, implied or otherwise.
|
|
|
|
-- 'Copper' networking test implementation.
|
|
-- This is meant as a portable (even into OC) library for networking.
|
|
-- This 'outer function' is the instantiator.
|
|
|
|
-- Note that it is probably possible to cause this code to run out of
|
|
-- memory in several hilarious ways.
|
|
|
|
-- Interfaces have no meaning, since addresses are names.
|
|
-- Which "side" a system is on is irrelevant.
|
|
-- For sending, the following function is used:
|
|
-- transmit(nodeId, message)
|
|
-- The nodeId is a string or number that has been given via culib.input,
|
|
-- or nil for broadcast.
|
|
-- It's more of a suggestion than a requirement to check nodeId.
|
|
|
|
-- The message is always a string.
|
|
-- This mirrors the message format usable by sendPacket and onReceive.
|
|
|
|
-- "onReceive" is a function which is called when a packet is decoded.
|
|
-- onReceive(namefrom, nameto, data)
|
|
|
|
-- "time" is a function which returns the real time, in seconds.
|
|
-- It need not be precise.
|
|
-- (This is used for caches.)
|
|
return function (hostname, transmit, onReceive, time)
|
|
|
|
-- How many packets need to be stored in seenBefore's keyspace
|
|
-- before 'panic' is the best response?
|
|
local tuningMaxSeenBeforeCountBeforeEmergencyFlush = 0x300
|
|
|
|
-- Expect another packet after this amount of time,
|
|
-- or else clear the known receivers cache entry.
|
|
-- Minimum should be less or equal to tuningAttempts *
|
|
-- tuningAttemptTime in relib.
|
|
local tuningExpectContinue = 15 + math.random(15)
|
|
|
|
-- Flush the loop detector every so often.
|
|
-- This is not a complete clear.
|
|
local tuningFlushLoopDetector = 60
|
|
|
|
-- Do not change this value unless protocol has changed accordingly.
|
|
local tuningAutorejectLen = 1506
|
|
|
|
local loopDetectorNext = time() + tuningFlushLoopDetector
|
|
|
|
-- Packets that have been seen before.
|
|
-- The values are the amount of times a packet has been seen.
|
|
-- This is flushed every tuningFlushLoopDetector seconds -
|
|
-- the flushing decrements the value until it reaches 0,
|
|
-- so packets which have looped before get a longer timeout.
|
|
local seenBefore = {}
|
|
local seenBeforeCount = 0
|
|
|
|
-- [address] = {
|
|
-- node, -- the node that a message was received from
|
|
-- expiry
|
|
-- }
|
|
local lastKnownReceiver = {}
|
|
|
|
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 refresh()
|
|
local t = time()
|
|
if t >= loopDetectorNext then
|
|
for k, v in pairs(seenBefore) do
|
|
local n = v - 1
|
|
if n > 0 then
|
|
seenBefore[k] = n
|
|
else
|
|
seenBefore[k] = nil
|
|
seenBeforeCount = seenBeforeCount - 1
|
|
end
|
|
end
|
|
loopDetectorNext = t + tuningFlushLoopDetector
|
|
end
|
|
for k, v in pairs(lastKnownReceiver) do
|
|
if t >= v[2] then
|
|
--print("It was decided LKV[" .. k .. "] was out of date @ " .. v[2] .. " by " .. hostname)
|
|
lastKnownReceiver[k] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
local culib = {}
|
|
|
|
-- Can be changed.
|
|
culib.hostname = hostname
|
|
|
|
-- Stats.
|
|
culib.lkrCacheMisses = 0
|
|
culib.lkrCacheHits = 0
|
|
|
|
culib.input = function (node, message)
|
|
local t = time()
|
|
|
|
-- Eliminate the hops value first of all.
|
|
local hops = message:byte(1)
|
|
message = message:sub(2)
|
|
|
|
if seenBefore[message] then
|
|
seenBefore[message] = seenBefore[message] + 1
|
|
return
|
|
else
|
|
seenBefore[message] = 2
|
|
seenBeforeCount = seenBeforeCount + 1
|
|
if seenBeforeCount > tuningMaxSeenBeforeCountBeforeEmergencyFlush then
|
|
-- Panic
|
|
seenBeforeCount = 0
|
|
seenBefore = {}
|
|
end
|
|
end
|
|
-- Begin parsing.
|
|
|
|
local rawmessage = message
|
|
|
|
if message:len() < 2 then return end
|
|
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
|
|
end
|
|
|
|
if fnam ~= "*" then
|
|
lastKnownReceiver[fnam] = {node, t + tuningExpectContinue}
|
|
end
|
|
|
|
onReceive(fnam, tnam, message)
|
|
if culib.hostname == tnam then return end
|
|
|
|
-- Redistribution of messages not aimed here
|
|
if hops == 255 then
|
|
return
|
|
else
|
|
rawmessage = string.char(hops + 1) .. rawmessage
|
|
end
|
|
|
|
local lkr = lastKnownReceiver[tnam]
|
|
if lkr then
|
|
culib.lkrCacheHits = culib.lkrCacheHits + 1
|
|
transmit(lkr[1], rawmessage)
|
|
else
|
|
culib.lkrCacheMisses = culib.lkrCacheMisses + 1
|
|
transmit(nil, rawmessage)
|
|
end
|
|
end
|
|
culib.refresh = refresh
|
|
culib.output = function (fnam, tnam, message)
|
|
onReceive(fnam, tnam, message)
|
|
if tnam == culib.hostname then return end
|
|
local m = "\x00" .. encodeName(fnam) .. encodeName(tnam) .. message
|
|
transmit(nil, m)
|
|
end
|
|
return culib
|
|
end
|