diff --git a/src/main/resources/assets/opencomputers/loot/Network/autorun.lua b/src/main/resources/assets/opencomputers/loot/Network/autorun.lua new file mode 100644 index 000000000..14b73dcd7 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/autorun.lua @@ -0,0 +1,7 @@ +local filesystem = require("filesystem") +local shell = require("shell") + +local args = {...} +shell.setPath(shell.getPath() .. ":/mnt/"..args[1].address:sub(1,3).."/") +--shell.execute("installNetwork") + diff --git a/src/main/resources/assets/opencomputers/loot/Network/data/bin/ifconfig.lua b/src/main/resources/assets/opencomputers/loot/Network/data/bin/ifconfig.lua new file mode 100644 index 000000000..f45b0c9c2 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/data/bin/ifconfig.lua @@ -0,0 +1,32 @@ +local network = require "network" +local computer = require "computer" +local args = {...} + +local function align(txt)return txt .. (" "):sub(#txt+1)end + +if #args < 1 then + print("Network interfaces:") + local info = network.info.getInfo() + for node, info in pairs(info.interfaces)do + print(align(node).."Link encap:"..info.linkName) + print(" HWaddr "..info.selfAddr) + if node == "lo" then print(" HWaddr "..computer.address()) end + local pktIn, pktOut, bytesIn, bytesOut = network.info.getInterfaceInfo(node) + print(" RX packets:"..tostring(pktIn)) + print(" TX packets:"..tostring(pktOut)) + print(" RX bytes:"..tostring(bytesIn).." TX bytes:"..tostring(bytesOut)) + end +elseif args[1] == "bind" and args[2] then + print("Address attached") + network.ip.bind(args[2]) +else + print("Usage:") + print(" ifconfig - view network summary") + print(" ifconfig bind [addr] - 'attach' addnitional address to computer") +end + + + + + + diff --git a/src/main/resources/assets/opencomputers/loot/Network/data/bin/ping.lua b/src/main/resources/assets/opencomputers/loot/Network/data/bin/ping.lua new file mode 100644 index 000000000..014c382b7 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/data/bin/ping.lua @@ -0,0 +1,21 @@ +local network = require "network" +local event = require "event" +local computer = require "computer" + +local args = {...} + +if #args < 1 then + print("Usage: ping: [addr]") +end + +print("Pinging "..args[1].." with 16 bytes of data:") +local pingID = network.icmp.ping(args[1], "0123456789abcdef") +local start = computer.uptime() + +local e, replier, id, payload +repeat +e, replier, id, payload = event.pull("ping_reply") +until id == pingID + +print("Got reply in "..tostring(computer.uptime()-start).." seconds") + diff --git a/src/main/resources/assets/opencomputers/loot/Network/data/boot/80_network.lua b/src/main/resources/assets/opencomputers/loot/Network/data/boot/80_network.lua new file mode 100644 index 000000000..0e37ea0e2 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/data/boot/80_network.lua @@ -0,0 +1,367 @@ +local event = require "event" +local computer = require "computer" +local network = require "network" + +local _rawSend +local isAccessible +local getNodes +local getInterfaceInfo +local startNetwork + +local dataHandler --Layer 2 data handler + +local posix + + +------------------------ +--Layer 1 + +local initated = false + +local function start() + if initated then return end + initated = true + + local computer = require "computer" + local filesystem = require "filesystem" + --local posix = require "posix" + + --------- + --Debug + --posix.verbose(print) + --------- + + + local accessibleHosts = {} + local nodes = {} + + --DRIVER INIT + --print("Loading drivers") + + local drivers = {} + + for file in filesystem.list("/lib/network") do + + --print("Loading driver:", file) + drivers[file] = {driver = loadfile("/lib/network/"..file)()} + + local eventHandler = {}--EVENT HANDLERS FOR DRIVER EVENTS + --eventHandler.debug = print + eventHandler.debug = function()end + + function eventHandler.newHost(node, address)--New interface in net node + --print("New host: ",node, address) + accessibleHosts[address] = {driver = drivers[file], node = node} + nodes[node].hosts[address] = address--mark host in node + end + + function eventHandler.newInterface(interface, selfAddr, linkName)--New node + --print("New interface: ",interface, selfaddr) + nodes[interface] = {hosts={}, driver = drivers[file], selfAddr = selfAddr, linkName = linkName} + end + + function eventHandler.recvData(data, node, origin) + dataHandler(data, node, origin) + end + + function eventHandler.setListener(evt, listener) + return event.listen(evt, function(...) + local args = {...} + local res = {pcall(function()listener(table.unpack(args))end)} + if not res[1] then + print("ERROR IN NET EVENTHANDLER["..file.."]:",res[2]) + end + return table.unpack(res,2) + end) + end + + drivers[file].handle = drivers[file].driver.start(eventHandler) + end + + _rawSend = function(addr, node, data) + --print("TrySend:",node,addr,":",data) + if accessibleHosts[addr] then + accessibleHosts[addr].driver.driver.send(accessibleHosts[addr].driver.handle, node, addr, data) + end + end + + isAccessible = function(addr) + if not accessibleHosts[addr] then return end + return accessibleHosts[addr].node, accessibleHosts[addr].driver + end + + getNodes = function() + return nodes + end + + getInterfaceInfo = function(interface) + if nodes[interface] then + return nodes[interface].driver.driver.info(interface) + end + end + + print("Link Control initated") + startNetwork() + print("Network initated") +end + +------------------------ +--Layer 2 + +startNetwork = function() + + local ttl = 32 + local rawSend + local send + + local routeRequests = {} -- Table by dest addressed of tables {type = T[, data=..]}, types: D(own waiting data), R(route request for someone), E(routed data we should be able to route..) + local routes = {} --Table of pairs -> [this or route] / {thisHost=true} / {router = [addr]} + + routes[computer.address()] = {thisHost=true} + + -----Utils + local function sizeToString(size) + return string.char((size)%256) .. string.char(math.floor(size/256)%256) .. string.char(math.floor(size/65536)%256) + end + + local function readSizeStr(str, pos) + local len = str:sub(pos,pos):byte() + return str:sub(pos+1, pos+len), len+1 + end + + local toByte = string.char + -----Data out + + local function onRecv(origin, data) + computer.pushSignal("network_message", origin, data) + end + + -----Sending + + local function sendDirectData(addr, data)--D[ttl-byte][data] + return rawSend(addr, "D"..toByte(ttl)..data) + end + + local function sendRoutedData(addr, data)--E[ttl-byte][hostlen-byte][dest host][hostlen-byte][origin host]message + local nodes = getNodes() + local msg = "E"..toByte(ttl)..toByte(addr:len())..addr..toByte(nodes[routes[addr].node].selfAddr:len())..nodes[routes[addr].node].selfAddr..data + _rawSend(routes[addr].router, routes[addr].node, msg) + end + + local function sendRoutedDataAs(addr, origin, data, ottl)--E[ttl-byte][hostlen-byte][dest host][hostlen-byte][origin host]message + local msg = "E"..toByte(ottl-1)..toByte(addr:len())..addr..toByte(origin:len())..origin..data + _rawSend(routes[addr].router, routes[addr].node, msg) + end + + local function sendRouteRequest(addr)--R[ttl-byte][Addr len][Requested addr][Route hosts-byte][ [addrLen-byte][addr] ] + local base = "R"..toByte(ttl)..toByte(addr:len())..addr..toByte(1) + local nodes = getNodes() + local sent = {} + for node, n in pairs(nodes) do + for host in pairs(n.hosts)do + if not sent[host]then + sent[host] = true + _rawSend(host, node, base..toByte(n.selfAddr:len())..n.selfAddr) + end + end + end + sent = nil + end + + local function resendRouteRequest(orig, node, host, nttl)--R[ttl-byte][Addr len][Requested addr][Route hosts-byte][ [addrLen-byte][addr] ] + local nodes = getNodes() + local hlen = orig:sub(3,3):byte() + + --local msg = "R" .. toByte(nttl) .. toByte(hlen+1) .. orig:sub(pos+4) .. toByte(nodes[node].selfAddr) .. nodes[node].selfAddr --broken, TODO repair + local msg = "R" .. toByte(nttl) .. orig:sub(3) --workaround + _rawSend(host, node, msg) + end + + local function sendHostFound(dest, addr)--H[ttl-byte][Found host] + return rawSend(dest, "H"..toByte(ttl)..addr) + end + + rawSend = function(addr, data) + local node, driver = isAccessible(addr) + if node then + _rawSend(addr, node, data) + return true + end + return false + end + + send = function(addr, data) + if type(addr) ~= "string" then error("Address must be string!!") end + if not sendDirectData(addr, data) then--Try send directly + if routes[addr] then + if routes[addr].thisHost then + onRecv("localhost", data)--it's this host, use loopback + else + sendRoutedData(addr, data)--We know route, try to send it that way + end + else + --route is unknown, we have to request it if we havent did it already + if not routeRequests[addr] then + routeRequests[addr] = {} + routeRequests[addr][#routeRequests[addr]+1] = {type = "D", data = data} + sendRouteRequest(addr) + else + routeRequests[addr][#routeRequests[addr]+1] = {type = "D", data = data} + end + end + end + end + + local function processRouteRequests(host) + if routeRequests[host] then + for _, request in pairs(routeRequests[host]) do + if request.type == "D" then + sendRoutedData(host, request.data) + elseif request.type == "E" then + if request.ttl-1 > 1 then + sendRoutedDataAs(host, request.origin, request.data, request.ttl) + end + elseif request.type == "R" then + sendHostFound(request.host, host) + end + end + routeRequests[host] = nil + end + end + + local function checkRouteDest(dest, origin, node, data) + local nodes = getNodes() + if dest == nodes[node].selfAddr then + return true + elseif routes[dest] and routes[dest].thisHost then + return true + end + return false + end + + bindAddr = function(addr) + routes[addr] = {thisHost=true} + processRouteRequests(addr) + end + + dataHandler = function(data, node, origin) + --print("DATA:", data, node, origin) + + if data:sub(1,1) == "D" then --Direct data + onRecv(origin, data:sub(3)) + elseif data:sub(1,1) == "E" then --Routed data + local ttl = data:byte(2) + local dest, destlen = readSizeStr(data, 3) + local orig, origlen = readSizeStr(data, 3+destlen) + local dat = data:sub(3+destlen+origlen) + if checkRouteDest(dest, orig, node, dat) then + onRecv(orig, dat) + else + if routes[dest] then + if ttl-1 > 0 then + sendRoutedDataAs(dest, orig, dat, ttl) + end + else + local _node, driver = isAccessible(dest) + if _node then + routes[dest] = {router = dest, node = _node} + if ttl-1 > 0 then + sendRoutedDataAs(dest, orig, dat, ttl) + end + else + if not routeRequests[dest] then routeRequests[dest] = {} end + routeRequests[dest][#routeRequests[dest]+1] = {type = "E", origin = orig, ttl = ttl, data = dat} + sendRouteRequest(dest) + end + end + end + elseif data:sub(1,1) == "R" then --Route request + local dest, l = readSizeStr(data, 3) + if not routeRequests[dest] then + + --check if accessible interface + local nodes = getNodes() + for _node, n in pairs(nodes) do + if _node ~= node then --requested host won't ever be in same node + for host in pairs(n.hosts)do + if host == dest then + --Found it! + sendHostFound(origin, dest) + return + end + end + end + end + + --check if route known + if routes[dest] then + if routes[dest].thisHost then + --sendHostFound(origin, nodes[node].selfAddr) + sendHostFound(origin, dest) + elseif routes[dest].router ~= origin then--Routen might have rebooted and is asking about route + --sendHostFound(origin, routes[dest].router) + sendHostFound(origin, dest) + end + return + end + + routeRequests[dest] = {} + routeRequests[dest][#routeRequests[dest]+1] = {type = "R", host = origin} + + local nttl = data:byte(2)-1 + if nttl > 1 then + local sent = {} + --Bcast request + for _node, n in pairs(nodes) do + if _node ~= node then --We mustn't send it to origin node + for host in pairs(n.hosts)do + if not sent[host] then + sent[host] = true + resendRouteRequest(data, _node, host, nttl) + end + end + end + end + end + sent = nil + else + --we've already requested this addr so if we get the route + --we'll respond + routeRequests[dest][#routeRequests[dest]+1] = {type = "R", host = origin} + end + elseif data:sub(1,1) == "H" then --Host found + local nttl = data:byte(2)-1 + local host = data:sub(3) + + if not routes[host] then + routes[host] = {router = origin, node = node} + processRouteRequests(host) + end + end + end + + network.core.setCallback("send", send) + network.core.setCallback("bind", bindAddr) + + --------------- + --Network stats&info + + local function getInfo() + local res = {} + + res.interfaces = {} + for k, node in pairs(getNodes())do + res.interfaces[k] = {selfAddr = node.selfAddr, linkName = node.linkName} + end + + return res + end + + network.core.setCallback("netstat", getInfo) + network.core.setCallback("intstat", getInterfaceInfo) + + network.core.lockCore() +end + +event.listen("init", start) + diff --git a/src/main/resources/assets/opencomputers/loot/Network/data/lib/network.lua b/src/main/resources/assets/opencomputers/loot/Network/data/lib/network.lua new file mode 100644 index 000000000..c5312a03a --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/data/lib/network.lua @@ -0,0 +1,222 @@ +local event = require "event" +local computer = require "computer" + +local driver = {} + +local network = {} +local internal = {} + +------------ +--Core communication +network.core = {} + +function network.core.setCallback(name, fn) + driver[name] = fn +end + +function network.core.lockCore() + network.core = nil +end + +------------ +--ICMP + +network.icmp = {} +internal.icmp = {} + +local pingid = 0 +function network.icmp.ping(addr, payload) + pingid = pingid + 1 + driver.send(addr, "IP"..computer.address()..":"..tostring(pingid)..":"..payload) + return pingid +end + +function internal.icmp.hanlde(origin, data) + if data:sub(2,2) == "P" then + local matcher = data:sub(3):gmatch("[^:]+") + local compid = matcher() + local id = tonumber(matcher()) + local payload = matcher() + if compid == computer.address() then + computer.pushSignal("ping_reply", origin, tonumber(id), payload) + else + driver.send(origin, data) + end + end +end + +------------ +--Datagrams - UDP like protocol + +network.udp = {} +internal.udp = {ports = {}} + +function internal.udp.checkPortRange(port) + if port < 0 or port > 65535 then error("Wrong port!")end +end + +function network.udp.open(port) + internal.udp.checkPortRange(port) + internal.udp.ports[port] = true +end + +function network.udp.close(port) + internal.udp.checkPortRange(port) + internal.udp.ports[port] = nil +end + +function network.udp.send(addr, port, data) + internal.udp.checkPortRange(port) + driver.send(addr, "D".. string.char(math.floor(port/256))..string.char(port%256)..data) +end + +function internal.udp.hanlde(origin, data) + local port = data:byte(2)*256 + data:byte(3) + if internal.udp.ports[port] then + computer.pushSignal("datagram", origin, port, data:sub(4)) + end +end + +----------- +--TCP - TCP like protocol + +--O[port,2B][openers channel,2B] --Try open connection +--A[opened channel,2B][openers channel,2B] --Accept connection +--R[openers channel,2B] --Reject connection i.e. closed port +--C[remote channel,2B] --Close connection(user request or adta at closed/wrong channel) +--D[remote channel,2B][data] --Data + +network.tcp = {} +internal.tcp = {ports = {}, channels = {}, freeCh = 1} + +function network.tcp.listen(port) + internal.udp.checkPortRange(port) + internal.tcp.ports[port] = true + +end + +function network.tcp.unlisten(port) + internal.udp.checkPortRange(port) + internal.tcp.ports[port] = nil +end + +function network.tcp.open(addr, port) + internal.udp.checkPortRange(port) + local ch = internal.tcp.freeCh + if internal.tcp.channels[ch] and internal.tcp.channels[ch].next then + internal.tcp.freeCh = internal.tcp.channels[ch].next + else + internal.tcp.freeCh = #internal.tcp.channels+1 + end + internal.tcp.channels[ch] = {open = false, waiting = true, addr = addr, port = port}--mark openning + + driver.send(addr, "TO".. string.char(math.floor(port/256))..string.char(port%256).. string.char(math.floor(ch/256))..string.char(ch%256)) + return ch +end + +function network.tcp.close(channel) + if internal.tcp.channels[channel] then + if internal.tcp.channels[channel].open or internal.tcp.channels[channel].waiting then + driver.send(addr, "TC".. string.char(math.floor(internal.tcp.channels[channel].remote/256))..string.char(internal.tcp.channels[channel].remote%256)) + end + internal.tcp.channels[channel] = {next = internal.tcp.freeCh} + internal.tcp.freeCh = channel + --computer.pushSignal("tcp_close", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port) + end +end + +function network.tcp.send(channel, data) + if internal.tcp.channels[channel] and internal.tcp.channels[channel].open then + driver.send(internal.tcp.channels[channel].addr, "TD".. string.char(math.floor(internal.tcp.channels[channel].remote/256))..string.char(internal.tcp.channels[channel].remote%256)..data) + return true + end + return false +end + +function internal.tcp.hanlde(origin, data) + if data:sub(2,2) == "O" then + local port = data:byte(3)*256 + data:byte(4) + local rchan = data:byte(5)*256 + data:byte(6) + + if internal.tcp.ports[port] then + local ch = internal.tcp.freeCh + if internal.tcp.channels[ch] and internal.tcp.channels[ch].next then + internal.tcp.freeCh = internal.tcp.channels[ch].next + else + internal.tcp.freeCh = #internal.tcp.channels+1 + end + internal.tcp.channels[ch] = {open = true, remote = rchan, addr = origin, port = port} + driver.send(origin, "TA".. string.char(math.floor(ch/256))..string.char(ch%256) .. string.char(math.floor(rchan/256)) .. string.char(rchan%256)) + computer.pushSignal("tcp", "connection", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port) + else + driver.send(origin, "TR".. string.char(math.floor(rchan/256))..string.char(rchan%256)) + end + elseif data:sub(2,2) == "R" then + local ch = data:byte(3)*256 + data:byte(4) + if internal.tcp.channels[ch] and internal.tcp.channels[ch].waiting then + internal.tcp.channels[ch] = {next = internal.tcp.freeCh} + internal.tcp.freeCh = ch + end + elseif data:sub(2,2) == "A" then + local remote = data:byte(3)*256 + data:byte(4) + local ch = data:byte(3)*256 + data:byte(4) + if internal.tcp.channels[ch] and internal.tcp.channels[ch].waiting then + internal.tcp.channels[ch].waiting = nil + internal.tcp.channels[ch].open = true + internal.tcp.channels[ch].remote = remote + computer.pushSignal("tcp", "connection", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port) + end + elseif data:sub(2,2) == "C" then + local ch = data:byte(3)*256 + data:byte(4) + if internal.tcp.channels[ch] and internal.tcp.channels[ch].open then + internal.tcp.channels[ch] = {next = internal.tcp.freeCh} + internal.tcp.freeCh = ch + computer.pushSignal("tcp" ,"close", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port) + end + elseif data:sub(2,2) == "D" then + local ch = data:byte(3)*256 + data:byte(4) + if internal.tcp.channels[ch] and internal.tcp.channels[ch].open then + computer.pushSignal("tcp", "message", ch, data:sub(5), internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port) + end + end +end + +----------- +--IP + +network.ip = {} + +function network.ip.bind(addr) + driver.bind(addr) +end + +------------ +--Data processing + +event.listen("network_message", function(_, origin, data) + --print("NETMSG/",origin,data) + if data:sub(1,1) == "I" then internal.icmp.hanlde(origin, data) + elseif data:sub(1,1) == "T" then internal.tcp.hanlde(origin, data) + elseif data:sub(1,1) == "D" then internal.udp.hanlde(origin, data) end + +end) + + +------------ +--Info + +network.info = {} +network.info.getInfo = function(...)return driver.netstat(...) end +network.info.getInterfaceInfo = function(...)return driver.intstat(...) end + +------------ + +return network + + + + + + + + diff --git a/src/main/resources/assets/opencomputers/loot/Network/data/lib/network/loopback.lua b/src/main/resources/assets/opencomputers/loot/Network/data/lib/network/loopback.lua new file mode 100644 index 000000000..1b7758dd7 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/data/lib/network/loopback.lua @@ -0,0 +1,27 @@ +local driver = {} + +local pktIn,pktOut,bytesIn,bytesOut = 0,0,0,0 + +function driver.start(eventHandler) + eventHandler.newInterface("lo", "localhost", "Local Loopback") + eventHandler.newHost("lo", "localhost") + return {send = function(data)eventHandler.recvData(data, "lo", "localhost")end} +end + +function driver.send(handle, interface, destination, data) + if interface == "lo" and destination == "localhost" then + pktIn, pktOut = pktIn+1,pktOut+1 + bytesIn,bytesOut = bytesIn + data:len(), bytesOut + data:len() + handle.send(data) + end +end + +function driver.info(interface) + if interface == "lo" then + return pktIn,pktOut,bytesIn,bytesOut + end + return 0,0,0,0 +end + +return driver + diff --git a/src/main/resources/assets/opencomputers/loot/Network/data/lib/network/modem.lua b/src/main/resources/assets/opencomputers/loot/Network/data/lib/network/modem.lua new file mode 100644 index 000000000..b5f76d399 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/data/lib/network/modem.lua @@ -0,0 +1,87 @@ +--[[ + +Communication on port 1! + +Node protocol: + +Hello/broadcast(sent by new host in node): H (modem addersses are in event) +Hi/direct(sent by hosts to new host): I (^) +Host quitting/broadcast Q (^) +Data/direct D[data] (origin from event) + +]] +local component = require "component" +local event = require "event" + +local driver = {} + +local nodes = {} +local eventHnd + +function driver.start(eventHandler) + eventHnd = eventHandler + local c = 0 + + eventHandler.setListener("modem_message", function(_, interface, origin, port, _, data) + if not nodes[interface] then return end --other kind of modem(possibly tunnel) + + eventHandler.debug("modemmsg["..nodes[interface].name.."]/"..origin..":"..data) + + nodes[interface].pktIn = nodes[interface].pktIn + 1 + nodes[interface].bytesIn = nodes[interface].bytesIn + data:len() + + if data:sub(1,1) == "H" then + eventHandler.newHost(nodes[interface].name, origin) + component.invoke(interface, "send", origin, 1, "I") + eventHandler.debug("REPL:",interface,origin) + elseif data:sub(1,1) == "I"then + eventHandler.newHost(nodes[interface].name, origin) + elseif data:sub(1,1) == "Q"then + eventHandler.delHost(nodes[interface].name, origin) + elseif data:sub(1,1) == "D"then + eventHandler.recvData(data:sub(2), nodes[interface].name, origin) + end + + end) + + for int in component.list("modem", true)do + eventHandler.newInterface("eth"..tostring(c), int, "Ethernet") + + nodes["eth"..tostring(c)] = {modem = int, name = "eth"..tostring(c), pktIn = 0, pktOut = 1, bytesIn = 0, bytesOut = 1} + nodes[int] = nodes["eth"..tostring(c)] + + component.invoke(int, "open", 1) + component.invoke(int, "broadcast", 1, "H") + + eventHandler.newHost("eth"..tostring(c), int)--register loopback + c = c + 1 + end + + --eventHandler.newHost("lo", "localhost") + return {} +end + +function driver.send(handle, interface, destination, data) + if nodes[interface] then + if nodes[interface].modem == destination then + nodes[interface].pktOut = nodes[interface].pktOut + 1 + nodes[interface].bytesOut = nodes[interface].bytesOut + data:len() + nodes[interface].pktIn = nodes[interface].pktIn + 1 + nodes[interface].bytesIn = nodes[interface].bytesIn + data:len() + eventHnd.recvData(data, interface, destination) + else + nodes[interface].pktOut = nodes[interface].pktOut + 1 + nodes[interface].bytesOut = nodes[interface].bytesOut + 1 + data:len() + component.invoke(nodes[interface].modem, "send", destination, 1, "D"..data) + end + end +end + +function driver.info(interface) + if nodes[interface] then + return nodes[interface].pktIn,nodes[interface].pktOut,nodes[interface].bytesIn,nodes[interface].bytesOut + end + return 0,0,0,0 +end + +return driver diff --git a/src/main/resources/assets/opencomputers/loot/Network/data/lib/network/tunnel.lua b/src/main/resources/assets/opencomputers/loot/Network/data/lib/network/tunnel.lua new file mode 100644 index 000000000..927bdd508 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/data/lib/network/tunnel.lua @@ -0,0 +1,77 @@ +local component = require "component" +local event = require "event" +--For protocol info look to at modem driver + +local driver = {} + +local nodes = {} +local eventHnd + +function driver.start(eventHandler) + eventHnd = eventHandler + local c = 0 + + eventHandler.setListener("modem_message", function(_, interface, origin, port, _, data) + if not nodes[interface] then return end --other kind of modem(possibly modem) + + eventHandler.debug("modemmsg["..nodes[interface].name.."]/"..origin..":"..data) + + nodes[interface].pktIn = nodes[interface].pktIn + 1 + nodes[interface].bytesIn = nodes[interface].bytesIn + data:len() + + if data:sub(1,1) == "H" then + eventHandler.newHost(nodes[interface].name, origin) + component.invoke(interface, "send", "I") + eventHandler.debug("REPL:",interface,origin) + elseif data:sub(1,1) == "I"then + eventHandler.newHost(nodes[interface].name, origin) + elseif data:sub(1,1) == "Q"then + eventHandler.delHost(nodes[interface].name, origin) + elseif data:sub(1,1) == "D"then + eventHandler.recvData(data:sub(2), nodes[interface].name, origin) + end + + end) + + for int in component.list("tunnel", true)do + eventHandler.newInterface("tun"..tostring(c), int, "Tunnel") + + nodes["tun"..tostring(c)] = {modem = int, name = "tun"..tostring(c), pktIn = 0, pktOut = 1, bytesIn = 0, bytesOut = 1} + nodes[int] = nodes["tun"..tostring(c)] + + component.invoke(int, "send", "H") + + eventHandler.newHost("tun"..tostring(c), int)--register loopback + c = c + 1 + end + + --eventHandler.newInterface("lo", "localhost", "Local Loopback") + --eventHandler.newHost("lo", "localhost") + return {} +end + +function driver.send(handle, interface, destination, data) + if nodes[interface] then + if nodes[interface].modem == destination then + nodes[interface].pktOut = nodes[interface].pktOut + 1 + nodes[interface].bytesOut = nodes[interface].bytesOut + data:len() + nodes[interface].pktIn = nodes[interface].pktIn + 1 + nodes[interface].bytesIn = nodes[interface].bytesIn + data:len() + eventHnd.recvData(data, interface, destination) + else + nodes[interface].pktOut = nodes[interface].pktOut + 1 + nodes[interface].bytesOut = nodes[interface].bytesOut + 1 + data:len() + component.invoke(nodes[interface].modem, "send", "D"..data) + end + end +end + +function driver.info(interface) + if nodes[interface] then + return nodes[interface].pktIn,nodes[interface].pktOut,nodes[interface].bytesIn,nodes[interface].bytesOut + end + return 0,0,0,0 +end + +return driver + diff --git a/src/main/resources/assets/opencomputers/loot/Network/data/usr/bin/nc.lua b/src/main/resources/assets/opencomputers/loot/Network/data/usr/bin/nc.lua new file mode 100644 index 000000000..99cbed8c8 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/data/usr/bin/nc.lua @@ -0,0 +1,55 @@ +local network = require "network" +local event = require "event" +local term = require "term" + +local args = {...} + +local listen = false +local port = -1 +local addr + +for _,par in ipairs(args) do + if par == "-l" then + listen = true + elseif port < 1 then + local p = tonumber(par) + if p then + port = p + end + else + addr = par + end +end + +if port < 0 then error("Unspecified port")end +if not listen and not addr then error("Unspecified address")end + + +local function handleTcp() + local channel + while true do + local e = {event.pull()} + if e[1] then + if e[1] == "tcp" then + if e[2] == "connection" then + channel = e[3] + if listen then network.tcp.unlisten(port)end + print("connected") + elseif e[2] == "message" then + term.write(e[4]) + end + elseif e[1] == "key_up" and channel then + network.tcp.send(channel, string.char(e[3])) + term.write(string.char(e[3])) + end + end + end +end + +if listen then + network.tcp.listen(port) + handleTcp() +else + network.tcp.open(addr, port) + handleTcp() +end diff --git a/src/main/resources/assets/opencomputers/loot/Network/data/usr/man/ifconfig b/src/main/resources/assets/opencomputers/loot/Network/data/usr/man/ifconfig new file mode 100644 index 000000000..c55a86d7a --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/data/usr/man/ifconfig @@ -0,0 +1,16 @@ +NAME + ifconfig - network information and configuration manager + +SYNOPSIS + ifconfig [command] [command arguments] + +DESCRIPTION + Displays information about current network status and configures its parts + +EXAMPLES + ifconfig + Output network status information + + ifconfig bind [address] + Binds addnitional address to this device. Address should match [A-Za-z0-9%-]+ and be max 64 characters length + diff --git a/src/main/resources/assets/opencomputers/loot/Network/data/usr/man/network b/src/main/resources/assets/opencomputers/loot/Network/data/usr/man/network new file mode 100644 index 000000000..5320090fc --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/data/usr/man/network @@ -0,0 +1,47 @@ +NAME + network - Advanced self-routing API + +METHODS + network.icmp.ping(addr, payload) + Sends ping message to addr, message will come back triggering + 'ping_reply' event + + network.ip.bind(addr) + Attaches addnitional address to this computer, useful for + core servers which addresses must me easisy rememberable + Address SOULD be up to 64 characters, allowed characters: a-zA-Z, '-' + + network.udp.open(port) + Starts listening on specified port, when data arrives at port "datagram" + event is triggered with origin, port, data parameters + + network.udp.close(port) + Stops listening on specified port + + network.udp.send(addr, port, data) + Sends data to specified host and port. Specified port MUST be open + on remote machine + + network.tcp.listen(port) + Starts listening at specified port. When connection arrives event + "tcp", "connection", channel, remoteaddr, port + is trigerred + + network.tcp.unlisten(port) + Stops listening on specified port. Note that all connections made to + this port will remain untouched + + network.tcp.open(addr, port) + Tries to open a new connection. Will trigger event + "tcp", "connection", channel, remoteaddr, port + When remote host accepted the connection + + network.tcp.close(channel) + Closes earlier opened connection, will trigger + "tcp", "close", channel, remoteaddr, port + on remote side + + network.tcp.send(channel, data) + Sends data to other side, will trigger + "tcp", "message", ch, data, remoteaddr, port + event on remote side diff --git a/src/main/resources/assets/opencomputers/loot/Network/data/usr/man/ping b/src/main/resources/assets/opencomputers/loot/Network/data/usr/man/ping new file mode 100644 index 000000000..4b24fdfe6 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/data/usr/man/ping @@ -0,0 +1,16 @@ +NAME + ping - check if host is reachable + +SYNOPSIS + ping [address] + +DESCRIPTION + Sends special ping packet to remote host and waits for answer + +EXAMPLES + ping localhost + Pings self + + ping somehost + Sends ping to somehost + diff --git a/src/main/resources/assets/opencomputers/loot/Network/installNetwork.lua b/src/main/resources/assets/opencomputers/loot/Network/installNetwork.lua new file mode 100644 index 000000000..323b859b4 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/installNetwork.lua @@ -0,0 +1,8 @@ +local filesystem = require "filesystem" +local shell = require "shell" +local process = require "process" +print("installNetwork") + +local status, installDir = shell.execute("install --noboot --nolabelset --name=Network --fromDir=/data/ --from="..filesystem.get(process.running()).address) + +print("You must reboot to start network services") diff --git a/src/main/resources/assets/opencomputers/loot/Network/protocol b/src/main/resources/assets/opencomputers/loot/Network/protocol new file mode 100644 index 000000000..359a230d4 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Network/protocol @@ -0,0 +1,84 @@ +MCNET +========= + +1. Definitions + 1. Network interface - any networking device that can send and recieve data and discover other cards in network node + 2. Network node - subnetwork of few compatibile network interfaces + 3. Address - unique name of network interface in network, by default unique id. One interface can have more than one address. + +2. Network layers + 1. Physical - Implemented by network interface drivers, is able to send and recieve data form other interface in node, is responsible for host discovery in node + 2. Network - Implements routing. + 3. Transport - Other protocols allowing simple data transfer + -This driver implements layer 2 and is interface to layer 1 implementations as well as part of layer 1 + +3. Layer implementation guidelines + 1. Physical + 1. Driver - Network interface driver is written in way libraries are written in LUA for OC. Each driver shold implement following methods: + 1. start(eventHandler): handle + Starts a networking device, returns driver-defined handler. eventHandler is a table provded by higher layer, containing callbacks to it. + 2. send(handle, interface, destination, data) + destination is address of another interface in node + data is string data to send + 3. stop(handle, interface) + Deactivates network interface. if interface is nil then driver should deactivate all its interfaces + 4. info(interface): pktIn, pktOut, bytesIn, bytesOut - usage stats + 2. Driver interface (or eventHandler) is table containing set of functions allowing lower layers to communicate with higher ones. The table must contain following methods: + 1. recvData(data, node, origin) - this function allows driver to pass incoming data to higher layer + 2. newInterface(interfaceAddr, selfAddr) - Register new interface. Interface is considered as node I/O, interface addr is internal node name + 3. delInterface(interfaceAddr) + 4. newHost(node, address) - this function must be called when new interface is detected in node + 5. delHost(node, address) - this function must be called when interface is removed from node + 6. setListener(evt, function) - event.listen wrapper + +4. Network layer packets: + Direct Data: + D[ttl-byte][data] + + Routed data: + E[ttl-byte][hostlen-byte][dest host][hostlen-byte][origin host]message + + Route Discovery: + R[ttl-byte][Addr len][Requested addr][Route hosts-byte][ [addrLen-byte][addr] ] + + Host found: + H[ttl-byte][Found host] + + Example case + + a. network: + + A G + | | + C--x--D--F--x + | | | + B E H + + b: nodes + 1. A,B,C,D + 2. D,E + 3. D,F + 4. F,G,H + + Example cases: + I. Network booted, nodes know hosts in them + 1. Host A sends message to host B + -Host A is hnown to host B, so host A sends to B direct message + -Network data: + A -> B: "Dmessage" + 2. Host A sends message to G + -Host A doesn't know host G so it sends route request to all hosts + host F knows G so it sends Host found packet back to A and another + Host found to G so it will be able to reply faster + -Network data: + A -> B,C,D: "R[32][1]G[1][1]A" + B,C = Connected to one node, do nothing + D -> E,F: "R[31][1]G[2][1]A[1]D" - also D save A to requester table of G + E = Connected to one node, but it's nice to know A + F -> D "H[32]G" + F -> G "H[32]A" + D -> A "H[31]G" - D had A in requester table of G so the packet is sent + A -> D "E[32][1]G[1]Amessage" + D -> F "E[31][1]G[1]Amessage" + F -> G "E[30][1]G[1]Amessage" + diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/install.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/install.lua index d83d4f516..dc94e2750 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/install.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/install.lua @@ -47,11 +47,12 @@ end candidates = nil local name = options.name or "OpenOS" -local origin = options.from and options.from:sub(1,3) or computer.getBootAddress():sub(1, 3) print("Installing " .. name .." to device " .. (choice.getLabel() or choice.address)) os.sleep(0.25) +local origin = options.from and options.from:sub(1,3) or computer.getBootAddress():sub(1, 3) +local fromDir = options.fromDir or "/" local mnt = choice.address:sub(1, 3) -local result, reason = os.execute("/bin/cp -vr /mnt/" .. origin .. "/* /mnt/" .. mnt .. "/") +local result, reason = os.execute("/bin/cp -vr /mnt/" .. origin .. fromDir .. "* /mnt/" .. mnt .. "/") if not result then error(reason, 0) end @@ -67,4 +68,6 @@ if not options.noreboot then end end print("Returning to shell.") +return "/mnt/" .. origin .. "/" + diff --git a/src/main/resources/assets/opencomputers/loot/loot.properties b/src/main/resources/assets/opencomputers/loot/loot.properties index 39c90ea9b..3fa73818a 100644 --- a/src/main/resources/assets/opencomputers/loot/loot.properties +++ b/src/main/resources/assets/opencomputers/loot/loot.properties @@ -6,10 +6,11 @@ # weight 1. The color defaults to gray. It must be a dye's ore-dict name. BetterShell=besh Builder=build:1:dyeCyan +MazeGen=maze:1:dyeBrown +Network=network:1:dyeYellow OpenIRC=irc:1:dyeRed OpenLoader=openloader:1:dyeOrange OpenOS=openos:1:dyeGreen -MazeGen=maze:1:dyeBrown OPPM=oppm:1:dyeLime # Higher chance to find the dig program, because it has the most immediate # use - OpenOS is craftable and IRC can be downloaded once an internet card