added a L3 implementation and updated the documentation

This commit is contained in:
Izaya 2018-02-16 21:36:00 +11:00
parent 6f21038b0d
commit f34936c10c
2 changed files with 256 additions and 0 deletions

213
OpenOS/etc/rc.d/minitel.lua Normal file
View File

@ -0,0 +1,213 @@
--[[
packet format:
packetID: random string to differentiate
packetType:
- 0: unreliable
- 1: reliable, requires ack
- 2: ack packet
destination: end destination hostname
sender: original sender of packet
data: the actual packet data, duh.
]]--
local listeners = {}
local timers = {}
local event = require "event"
local component = require "component"
local computer = require "computer"
local hostname = computer.address()
local listener = false
local dbug = false
local modems = {}
local port = 4096
local retry = 30
--[[
LKR format:
address {
local hardware address
remote hardware address
time last received
}
]]--
local rcache = {}
local rctime = 30
--[[
packet queue format:
{
packetID,
packetType
destination,
data,
timestamp,
attempts
}
]]--
local pqueue = {}
-- packet cache: [packet ID]=uptime
local pcache = {}
local pctime = 30
local function dprint(...)
if dbug then
print(...)
end
end
function start()
local f=io.open("/etc/hostname","rb")
if f then
hostname = f:read()
f:close()
end
print("Hostname: "..hostname)
if listener then return end
for a,t in component.list("modem") do
modems[#modems+1] = component.proxy(a)
end
for k,v in ipairs(modems) do
v.open(port)
print("Opened port "..port.." on "..v.address)
end
local function genPacketID()
local npID = ""
for i = 1, 16 do
npID = npID .. string.char(math.random(32,126))
end
return npID
end
local function sendPacket(packetID,packetType,dest,sender,vport,data)
if rcache[dest] then
dprint("Cached", rcache[dest][1],"send",rcache[dest][2],port,packetID,packetType,dest,sender,vport,data)
component.invoke(rcache[dest][1],"send",rcache[dest][2],port,packetID,packetType,dest,sender,vport,data)
else
dprint("Not cached", port,packetID,packetType,dest,sender,vport,data)
for k,v in pairs(modems) do
v.broadcast(port,packetID,packetType,dest,sender,vport,data)
end
end
end
local function pruneCache()
for k,v in pairs(rcache) do
dprint(k,v[3],computer.uptime())
if v[3] < computer.uptime() then
rcache[k] = nil
dprint("pruned "..k.." from routing cache")
end
end
for k,v in pairs(pcache) do
if v < computer.uptime() then
pcache[k] = nil
dprint("pruned "..k.." from packet cache")
end
end
end
local function checkPCache(packetID)
dprint(packetID)
for k,v in pairs(pcache) do
dprint(k)
if k == packetID then return true end
end
return false
end
local function processPacket(_,localModem,from,pport,_,packetID,packetType,dest,sender,vport,data)
pruneCache()
if pport == port then
dprint(port,vport,packetType,dest)
if checkPCache(packetID) then return end
if dest == hostname then
if packetType == 1 then
sendPacket(genPacketID(),2,sender,hostname,vport,packetID)
end
if packetType == 2 then
dprint("Dropping "..data.." from queue")
pqueue[data] = nil
computer.pushSignal("net_ack",data)
end
if packetType ~= 2 then
computer.pushSignal("net_msg",sender,vport,data)
end
end
if not rcache[sender] then
dprint("rcache: "..sender..":", localModem,from,computer.uptime())
rcache[sender] = {localModem,from,computer.uptime()+rctime}
end
if not pcache[packetID] then
pcache[packetID] = computer.uptime()+pctime
end
end
end
listeners["modem_message"]=processPacket
event.listen("modem_message",processPacket)
print("Started packet listening daemon: "..tostring(processPacket))
local function queuePacket(_,ptype,to,vport,data)
npID = genPacketID()
pqueue[npID] = {ptype,to,vport,data,0,0}
dprint(npID,table.unpack(pqueue[npID]))
end
listeners["net_send"]=queuePacket
event.listen("net_send",queuePacket)
print("Started packet queueing daemon: "..tostring(queuePacket))
local function packetPusher()
for k,v in pairs(pqueue) do
if v[5] < computer.uptime() then
dprint(k,v[1],v[2],hostname,v[3],v[4])
sendPacket(k,v[1],v[2],hostname,v[3],v[4])
if v[1] ~= 1 or v[6] == 255 then
pqueue[k] = nil
else
pqueue[k][5]=computer.uptime()+retry
pqueue[k][6]=pqueue[k][6]+1
end
end
end
end
timers[#timers+1]=event.timer(0,packetPusher,math.huge)
print("Started packet pusher: "..tostring(timers[#timers]))
event.listen("net_ack",dprint)
end
function stop()
for k,v in pairs(listeners) do
event.ignore(k,v)
print("Stopped listener: "..tostring(v))
end
for k,v in pairs(timers) do
event.cancel(v)
print("Stopped timer: "..tostring(v))
end
end
function debug()
dbug = not dbug
end
function set_retry(sn)
retry = tonumber(sn) or 30
print("retry = "..tostring(retry))
end
function set_pctime(sn)
pctime = tonumber(sn) or 30
print("pctime = "..tostring(pctime))
end
function set_rctime(sn)
rctime = tonumber(sn) or 30
print("rctime = "..tostring(rctime))
end
function set_port(sn)
port = tonumber(sn) or 4096
print("port = "..tostring(port))
end

43
protocol.md Normal file
View File

@ -0,0 +1,43 @@
# Minitel - An easy to implement LAN protocol
## Language
Node - a device of some description on a Minitel network, ie a computer, server, microcontroller or drone.
## Behavior
### Receiving
Upon a node receiving a message addressed to itself, it should:
1. Check if the packet ID has been seen before, and if so, drop the packet
2. Check if the packet is addressed to the node, and if so, queue it as a net_msg event
3. If the packet is indeed addressed to this node, and the packet type is 1 (reliable), send an acknowledgement packet.
4. Optional: Add the sender to the address cache if it isn't already in the cache
5. Optional: If the packet is addressed to a different node, repeat the packet, preferably respecting the address cache
### Optional: Meshing
If a message is not addressed to a node, and the node has not seen the packet ID before, the node should repeat it. Whether via the address in the cache or by broadcast, it should be passed on, and the hardware address added to the cache as the sender.
### Optional: Address caching
Each machine should keep a last known route cache. The exact format is up to the implementation, but it will need:
- hostname
- hardware address
- time when added to cache
It is recommended to keep the data in the cache for 30 seconds or so, then drop it, though being user-configurable is ideal.
When sending a message, check the cache for the given destination. If there is a hardware address in the cache for the destination, send the message straight to that address. Otherwise, broadcast the message.
## Packet Format
Packets are made up of separated parts, as allowed by OpenComputers modems.
- packetID: random string to differentiate packets from each other
- packetType, number:
0. unreliable
1. reliable, requires acknowledgement
2. acknowledgement packet
- destination: end destination hostname, string
- sender: original sender of packet, string
- port: virtual port, number \< 65536
- data: the actual packet data, or in the case of an acknowledgement packet, the original packet ID, string