Operating system for OpenComputers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

275 lines
6.5KB

  1. --[[
  2. packet format:
  3. packetID: random string to differentiate
  4. packetType:
  5. - 0: unreliable
  6. - 1: reliable, requires ack
  7. - 2: ack packet
  8. destination: end destination hostname
  9. sender: original sender of packet
  10. data: the actual packet data, duh.
  11. ]]--
  12. local listeners = {}
  13. local timers = {}
  14. local cfg = {}
  15. local event = require "event"
  16. local component = require "component"
  17. local computer = require "computer"
  18. local serial = require "serialization"
  19. local hostname = computer.address():sub(1,8)
  20. local modems = {}
  21. local pid = nil
  22. cfg.debug = false
  23. cfg.port = 4096
  24. cfg.retry = 10
  25. cfg.retrycount = 3
  26. cfg.route = true
  27. --[[
  28. LKR format:
  29. address {
  30. local hardware address
  31. remote hardware address
  32. time last received
  33. }
  34. ]]--
  35. cfg.sroutes = {}
  36. local rcache = setmetatable({},{__index=cfg.sroutes})
  37. cfg.rctime = 15
  38. --[[
  39. packet queue format:
  40. {
  41. packetID,
  42. packetType
  43. destination,
  44. data,
  45. timestamp,
  46. attempts
  47. }
  48. ]]--
  49. local pqueue = {}
  50. -- packet cache: [packet ID]=uptime
  51. local pcache = {}
  52. cfg.pctime = 30
  53. local function dprint(...)
  54. if cfg.debug then
  55. print(...)
  56. end
  57. end
  58. local function saveconfig()
  59. local f = io.open("/boot/cfg/minitel.cfg","wb")
  60. if f then
  61. f:write(serial.serialize(cfg))
  62. f:close()
  63. end
  64. end
  65. local function loadconfig()
  66. local f = io.open("/boot/cfg/minitel.cfg","rb")
  67. if f then
  68. local newcfg = serial.unserialize(f:read("*a"))
  69. f:close()
  70. for k,v in pairs(newcfg) do
  71. cfg[k] = v
  72. end
  73. else
  74. saveconfig()
  75. end
  76. end
  77. function start()
  78. loadconfig()
  79. hostname = os.getenv("HOSTNAME") or computer.address():sub(1,8)
  80. print("Hostname: "..hostname)
  81. if pid then return false end
  82. modems={}
  83. for a,t in component.list("modem") do
  84. modems[#modems+1] = component.proxy(a)
  85. end
  86. for k,v in ipairs(modems) do
  87. v.open(cfg.port)
  88. print("Opened port "..cfg.port.." on "..v.address)
  89. end
  90. for a,t in component.list("tunnel") do
  91. modems[#modems+1] = component.proxy(a)
  92. end
  93. local function genPacketID()
  94. local npID = ""
  95. for i = 1, 16 do
  96. npID = npID .. string.char(math.random(32,126))
  97. end
  98. return npID
  99. end
  100. local function sendPacket(packetID,packetType,dest,sender,vPort,data,repeatingFrom)
  101. if rcache[dest] then
  102. dprint("Cached", rcache[dest][1],"send",rcache[dest][2],cfg.port,packetID,packetType,dest,sender,vPort,data)
  103. if component.type(rcache[dest][1]) == "modem" then
  104. component.invoke(rcache[dest][1],"send",rcache[dest][2],cfg.port,packetID,packetType,dest,sender,vPort,data)
  105. elseif component.type(rcache[dest][1]) == "tunnel" then
  106. component.invoke(rcache[dest][1],"send",packetID,packetType,dest,sender,vPort,data)
  107. end
  108. else
  109. dprint("Not cached", cfg.port,packetID,packetType,dest,sender,vPort,data)
  110. for k,v in pairs(modems) do
  111. -- do not send message back to the wired or linked modem it came from
  112. -- the check for tunnels is for short circuiting `v.isWireless()`, which does not exist for tunnels
  113. if v.address ~= repeatingFrom or (v.type ~= "tunnel" and v.isWireless()) then
  114. if v.type == "modem" then
  115. v.broadcast(cfg.port,packetID,packetType,dest,sender,vPort,data)
  116. v.send(packetID,packetType,dest,sender,vPort,data)
  117. end
  118. end
  119. end
  120. end
  121. end
  122. local function pruneCache()
  123. for k,v in pairs(rcache) do
  124. dprint(k,v[3],computer.uptime())
  125. if v[3] < computer.uptime() then
  126. rcache[k] = nil
  127. dprint("pruned "..k.." from routing cache")
  128. end
  129. end
  130. for k,v in pairs(pcache) do
  131. if v < computer.uptime() then
  132. pcache[k] = nil
  133. dprint("pruned "..k.." from packet cache")
  134. end
  135. end
  136. end
  137. local function checkPCache(packetID)
  138. dprint(packetID)
  139. for k,v in pairs(pcache) do
  140. dprint(k)
  141. if k == packetID then return true end
  142. end
  143. return false
  144. end
  145. local function processPacket(_,localModem,from,pport,_,packetID,packetType,dest,sender,vPort,data)
  146. pruneCache()
  147. if pport == cfg.port or pport == 0 then -- for linked cards
  148. dprint(cfg.port,vPort,packetType,dest)
  149. if checkPCache(packetID) then return end
  150. if dest == hostname then
  151. if packetType == 1 then
  152. sendPacket(genPacketID(),2,sender,hostname,vPort,packetID)
  153. end
  154. if packetType == 2 then
  155. dprint("Dropping "..data.." from queue")
  156. pqueue[data] = nil
  157. computer.pushSignal("net_ack",data)
  158. end
  159. if packetType ~= 2 then
  160. computer.pushSignal("net_msg",sender,vPort,data)
  161. end
  162. elseif dest:sub(1,1) == "~" then -- broadcasts start with ~
  163. computer.pushSignal("net_broadcast",sender,vPort,data)
  164. elseif cfg.route then -- repeat packets if route is enabled
  165. sendPacket(packetID,packetType,dest,sender,vPort,data,localModem)
  166. end
  167. if not rcache[sender] then -- add the sender to the rcache
  168. dprint("rcache: "..sender..":", localModem,from,computer.uptime())
  169. rcache[sender] = {localModem,from,computer.uptime()+cfg.rctime}
  170. end
  171. if not pcache[packetID] then -- add the packet ID to the pcache
  172. pcache[packetID] = computer.uptime()+cfg.pctime
  173. end
  174. end
  175. end
  176. local function queuePacket(_,ptype,to,vPort,data,npID)
  177. npID = npID or genPacketID()
  178. if to == hostname or to == "localhost" then
  179. computer.pushSignal("net_msg",to,vPort,data)
  180. computer.pushSignal("net_ack",npID)
  181. return
  182. end
  183. pqueue[npID] = {ptype,to,vPort,data,0,0}
  184. dprint(npID,table.unpack(pqueue[npID]))
  185. end
  186. local function packetPusher()
  187. for k,v in pairs(pqueue) do
  188. if v[5] < computer.uptime() then
  189. dprint(k,v[1],v[2],hostname,v[3],v[4])
  190. sendPacket(k,v[1],v[2],hostname,v[3],v[4])
  191. if v[1] ~= 1 or v[6] == cfg.retrycount then
  192. pqueue[k] = nil
  193. else
  194. pqueue[k][5]=computer.uptime()+cfg.retry
  195. pqueue[k][6]=pqueue[k][6]+1
  196. end
  197. end
  198. end
  199. end
  200. listeners["modem_message"]=processPacket
  201. listeners["net_send"]=queuePacket
  202. listeners["net_ack"]=dprint
  203. pid=os.spawn(function()
  204. while true do
  205. local ev = {coroutine.yield()}
  206. packetPusher()
  207. pruneCache()
  208. if listeners[ev[1]] then
  209. pcall(listeners[ev[1]],table.unpack(ev))
  210. end
  211. end
  212. end,"minitel")
  213. print("Started Minitel daemon: "..tostring(pid))
  214. return pid
  215. end
  216. function stop()
  217. if pid then
  218. os.kill(pid)
  219. pid = nil
  220. return true
  221. else
  222. return false
  223. end
  224. end
  225. function set(k,v)
  226. if type(cfg[k]) == "string" then
  227. cfg[k] = v
  228. elseif type(cfg[k]) == "number" then
  229. cfg[k] = tonumber(v)
  230. elseif type(cfg[k]) == "boolean" then
  231. if v:lower():sub(1,1) == "t" then
  232. cfg[k] = true
  233. else
  234. cfg[k] = false
  235. end
  236. end
  237. print("cfg."..k.." = "..tostring(cfg[k]))
  238. saveconfig()
  239. end
  240. function set_route(to,laddr,raddr)
  241. cfg.sroutes[to] = {laddr,raddr,0}
  242. saveconfig()
  243. end
  244. function del_route(to)
  245. cfg.sroutes[to] = nil
  246. saveconfig()
  247. end
  248. return {start=start,stop=stop,set=set,set_route=set_route,del_route=del_route}