mirror of
https://github.com/AR2000AR/openComputers_codes.git
synced 2025-09-08 14:41:14 +02:00
[osinetwork] rfc 791 (almost)
This commit is contained in:
@@ -97,7 +97,7 @@ function IPv4Layer:mask(value)
|
||||
return oldValue
|
||||
end
|
||||
|
||||
function IPv4Layer:mtu() return self:layer():mtu() - 38 end
|
||||
function IPv4Layer:mtu() return self:layer():mtu() - string.packsize(IPv4Packet.headerFormat) end
|
||||
|
||||
---@param value? IPv4Router
|
||||
---@return IPv4Router
|
||||
@@ -138,40 +138,49 @@ function IPv4Layer:send(to, payload)
|
||||
end
|
||||
end
|
||||
|
||||
---@param from string
|
||||
---@param to string
|
||||
---@param from? string
|
||||
---@param to? string
|
||||
---@param payload string
|
||||
function IPv4Layer:payloadHandler(from, to, payload)
|
||||
checkArg(1, from, 'string')
|
||||
checkArg(2, to, 'string')
|
||||
checkArg(1, from, 'string','nil')
|
||||
checkArg(2, to, 'string','nil')
|
||||
checkArg(3, payload, 'string')
|
||||
local pl = IPv4Packet.unpack(payload)
|
||||
if (pl:dst() == self:addr()) then
|
||||
if (pl:len() > 1) then --merge framents
|
||||
local bufferID = string.format("%d%d%d%d", from, to, pl:protocol(), pl:id())
|
||||
if (bit32.btest(pl:flags(), ipv4Consts.FLAGS.MF --[[MF]]) or pl:fragmentOffset() > 0) then
|
||||
--fragmented packet
|
||||
local bufferID = string.pack('>IIH', pl:src(), pl:dst(), pl:id())
|
||||
--Store the fragments
|
||||
self._buffer[bufferID] = self._buffer[bufferID] or {}
|
||||
|
||||
--place the packet in a buffer
|
||||
table.insert(self._buffer[bufferID], math.max(#self._buffer[bufferID], pl:fragmentOffset()), pl)
|
||||
|
||||
--if the buffer hold all the packets merge them
|
||||
if (#self._buffer[bufferID] == pl:len()) then
|
||||
local fullPayload, proto = {}, pl:protocol()
|
||||
for i, fragment in ipairs(self._buffer[pl:protocol()][pl:src()]) do
|
||||
table.insert(fullPayload, math.max(#fullPayload, fragment:fragmentOffset()), fragment:payload())
|
||||
self._buffer[bufferID][pl:fragmentOffset()] = pl
|
||||
--Check if we have a full packet
|
||||
if (not bit32.btest(pl:flags(), ipv4Consts.FLAGS.MF)) then
|
||||
local newPayload = ''
|
||||
local reassembled = true
|
||||
for k, v in pairs(self._buffer[bufferID]) do
|
||||
---@cast v IPv4Packet
|
||||
if (k ~= #newPayload) then
|
||||
--missing a fragment
|
||||
--TODO : send reassembly error
|
||||
reassembled = false
|
||||
break
|
||||
end
|
||||
newPayload = newPayload .. v:payload()
|
||||
end
|
||||
if (reassembled) then
|
||||
pl:payload(newPayload)
|
||||
pl:flags(bit32.band(pl:flags(), bit32.bnot(ipv4Consts.FLAGS.MF)))
|
||||
pl:fragmentOffset(0)
|
||||
end
|
||||
pl = IPv4Packet(pl:src(), pl:dst(), table.concat(fullPayload), pl:protocol())
|
||||
pl:protocol(proto)
|
||||
self._buffer[pl:protocol()][pl:src()] = nil
|
||||
end
|
||||
--TODO : handle merge timeout
|
||||
end
|
||||
if (pl:len() == 1) then
|
||||
if (not (bit32.btest(pl:flags(), ipv4Consts.FLAGS.MF --[[MF]])) and pl:fragmentOffset() == 0) then
|
||||
--if the packet is complete, send it to the router to be handed to the destination program
|
||||
self._router:payloadHandler(pl:src(), pl:dst(), pl:pack())
|
||||
end
|
||||
else
|
||||
--TODO : check if routing is enabled
|
||||
--TODO : may need extra fragmenting
|
||||
self._router:send(pl)
|
||||
end
|
||||
end
|
||||
|
@@ -1,18 +1,22 @@
|
||||
local bit32 = require("bit32")
|
||||
local ethernet = require("network.ethernet")
|
||||
local Payload = require("network.abstract.Payload")
|
||||
local utils = require("network.utils")
|
||||
local class = require("libClass2")
|
||||
|
||||
|
||||
---@class IPv4Header
|
||||
---@field version number 4 : ip version
|
||||
---@field ihl number 5 : internet header lengh
|
||||
---@field dscp number Differentiated Services Code Point
|
||||
---@field ecn number Explicit Congestion Notification
|
||||
---@field len number Total Length. In this implementation, indicate the number of framgments
|
||||
---@field len number Datagram lengh
|
||||
---@field id number Identification
|
||||
---@field flags number Flags bit 0: Reserved; must be zero. bit 1: Don't Fragment (DF) bit 2: More Fragments (MF)
|
||||
---@field fragmentOffset number Fragment offset. In this implementation, correspond to the framgments number/place
|
||||
---@field fragmentOffset number Fragment offset.
|
||||
---@field ttl number Time to live
|
||||
---@field protocol ipv4Protocol Protocol
|
||||
---@field checksum number header checksum
|
||||
---@field src number Source address
|
||||
---@field dst number Destination address
|
||||
|
||||
@@ -44,14 +48,17 @@ function IPv4Packet:new(src, dst, payload, protocole)
|
||||
local o = {
|
||||
---@type IPv4Header
|
||||
_header = {
|
||||
version = 4,
|
||||
ihl = 5,
|
||||
dscp = 0,
|
||||
ecn = 0,
|
||||
len = 1,
|
||||
--totalLengh
|
||||
id = 0,
|
||||
flags = 0,
|
||||
fragmentOffset = 0,
|
||||
ttl = 64,
|
||||
protocol = 1,
|
||||
--checksum = 0,
|
||||
src = 0,
|
||||
dst = 0,
|
||||
},
|
||||
@@ -86,6 +93,16 @@ function IPv4Packet:payload(value)
|
||||
return oldValue
|
||||
end
|
||||
|
||||
---@return number
|
||||
function IPv4Packet:version()
|
||||
return self._header.version
|
||||
end
|
||||
|
||||
---@return number
|
||||
function IPv4Packet:ihl()
|
||||
return self._header.ihl
|
||||
end
|
||||
|
||||
---@param value? number
|
||||
---@return number
|
||||
function IPv4Packet:dscp(value)
|
||||
@@ -104,13 +121,9 @@ function IPv4Packet:ecn(value)
|
||||
return oldValue
|
||||
end
|
||||
|
||||
---@param value? number
|
||||
---@return number
|
||||
function IPv4Packet:len(value)
|
||||
checkArg(1, value, 'number', 'nil')
|
||||
local oldValue = self._header.len
|
||||
if (value ~= nil) then self._header.len = value end
|
||||
return oldValue
|
||||
function IPv4Packet:len()
|
||||
return string.packsize(IPv4Packet.headerFormat) + #self:payload()
|
||||
end
|
||||
|
||||
---@param value? number
|
||||
@@ -158,6 +171,24 @@ function IPv4Packet:protocol(value)
|
||||
return oldValue
|
||||
end
|
||||
|
||||
---@param value? number
|
||||
---@return number
|
||||
function IPv4Packet:checksum(value)
|
||||
checkArg(1, value, 'number', 'nil')
|
||||
local oldValue = self._header.checksum or self:calculateChecksum()
|
||||
if (value ~= nil) then self._header.checksum = value end
|
||||
return oldValue
|
||||
end
|
||||
|
||||
---@return number
|
||||
function IPv4Packet:calculateChecksum()
|
||||
local versionAndIHL = bit32.lshift(self:version(), 4) + self:ihl()
|
||||
local dscpAndEcn = bit32.lshift(self:dscp(), 4) + self:ecn()
|
||||
local flagsAndFragOffset = bit32.lshift(self:flags(), 13) + self:fragmentOffset()
|
||||
local header = string.pack(self.headerFormat, versionAndIHL, dscpAndEcn, self:len(), self:id(), flagsAndFragOffset, self:ttl(), self:protocol(), 0, self:src(), self:dst())
|
||||
return utils.checksum(header)
|
||||
end
|
||||
|
||||
---@param value? number
|
||||
---@return number
|
||||
function IPv4Packet:src(value)
|
||||
@@ -182,6 +213,7 @@ end
|
||||
---@param maxFragmentSize number
|
||||
---@return table<IPv4Packet>
|
||||
function IPv4Packet:getFragments(maxFragmentSize)
|
||||
--TODO : currentPos may be != from 1
|
||||
local fragments = {}
|
||||
local fragmentID = 1;
|
||||
local fragmentTotal = math.ceil(#self:payload() / maxFragmentSize)
|
||||
@@ -197,8 +229,7 @@ function IPv4Packet:getFragments(maxFragmentSize)
|
||||
local framgentPacket = IPv4Packet(self:src(), self:dst(), currentFragment, self:protocol())
|
||||
table.insert(fragments, framgentPacket)
|
||||
framgentPacket:id(self:id())
|
||||
framgentPacket:fragmentOffset(#fragments)
|
||||
framgentPacket:len(fragmentTotal)
|
||||
framgentPacket:fragmentOffset(currentPos - 1)
|
||||
fragmentID = fragmentID + 1
|
||||
currentPos = currentPos + maxFragmentSize + 1
|
||||
if (fragmentID < fragmentTotal) then
|
||||
@@ -210,10 +241,15 @@ function IPv4Packet:getFragments(maxFragmentSize)
|
||||
return fragments
|
||||
end
|
||||
|
||||
IPv4Packet.payloadFormat = "xI1I1I2I1I1I2I1I1xxI4I4s"
|
||||
IPv4Packet.headerFormat = ">BBHHHBBHII"
|
||||
IPv4Packet.payloadFormat = IPv4Packet.headerFormat
|
||||
|
||||
function IPv4Packet:pack()
|
||||
return string.pack(self.payloadFormat, self:dscp(), self:ecn(), self:len(), self:id(), self:flags(), self:fragmentOffset(), self:ttl(), self:protocol(), self:src(), self:dst(), self:payload())
|
||||
local versionAndIHL = bit32.lshift(self:version(), 4) + self:ihl()
|
||||
local dscpAndEcn = bit32.lshift(self:dscp(), 4) + self:ecn()
|
||||
local flagsAndFragOffset = bit32.lshift(self:flags(), 13) + self:fragmentOffset()
|
||||
local header = string.pack(self.headerFormat, versionAndIHL, dscpAndEcn, self:len(), self:id(), flagsAndFragOffset, self:ttl(), self:protocol(), self:checksum(), self:src(), self:dst())
|
||||
return header .. string.pack('c' .. #self:payload(), self:payload())
|
||||
end
|
||||
|
||||
---@param val string
|
||||
@@ -221,27 +257,42 @@ end
|
||||
function IPv4Packet.unpack(val)
|
||||
checkArg(1, val, 'string')
|
||||
|
||||
local dscp, ecn, len, id, flags, fragmentOffset, ttl, protocol, src, dst, payload = string.unpack(IPv4Packet.payloadFormat, val)
|
||||
---@cast dscp number
|
||||
---@cast ecn number
|
||||
local versionAndIHL, dscpAndEcn, len, id, flagsAndFragmentOffset, ttl, protocol, checksum, src, dst, offset = string.unpack(IPv4Packet.payloadFormat, val)
|
||||
---@cast versionAndIHL number
|
||||
local version = bit32.extract(versionAndIHL, 4, 4)
|
||||
local ihl = bit32.extract(versionAndIHL, 0, 4)
|
||||
---@cast dscpAndEcn number
|
||||
local dscp = bit32.extract(dscpAndEcn, 2, 6)
|
||||
local ecn = bit32.extract(dscp, 0, 2)
|
||||
---@cast flagsAndFragmentOffset number
|
||||
local flags = bit32.extract(flagsAndFragmentOffset, 14, 3)
|
||||
local fragmentOffset = bit32.extract(flagsAndFragmentOffset, 0, 13)
|
||||
---@cast len number
|
||||
---@cast id number
|
||||
---@cast flags number
|
||||
---@cast fragmentOffset number
|
||||
---@cast ttl number
|
||||
---@cast protocol number
|
||||
---@cast checksum number
|
||||
---@cast src number
|
||||
---@cast dst number
|
||||
---@cast offset number
|
||||
|
||||
local payload = string.unpack('c' .. len - (ihl * 4), val, offset)
|
||||
---@cast payload string
|
||||
|
||||
local packet = IPv4Packet(src, dst, payload, protocol)
|
||||
--packet:version(version)
|
||||
--packet:ihl(ihl)
|
||||
packet:dscp(dscp)
|
||||
packet:ecn(ecn)
|
||||
packet:len(len)
|
||||
--packet:len(len)
|
||||
packet:id(id)
|
||||
packet:flags(flags)
|
||||
packet:fragmentOffset(fragmentOffset)
|
||||
packet:ttl(ttl)
|
||||
packet:protocol(protocol)
|
||||
packet:checksum(checksum)
|
||||
packet:src(src)
|
||||
packet:dst(dst)
|
||||
|
||||
return packet
|
||||
end
|
||||
|
@@ -9,4 +9,10 @@ ipv4Consts.PROTOCOLS = {
|
||||
OSPF = 89, -- Open Shortest Path First
|
||||
}
|
||||
|
||||
ipv4Consts.FLAGS = {
|
||||
UNUSED = 4,
|
||||
DF = 2,
|
||||
MF = 1
|
||||
}
|
||||
|
||||
return ipv4Consts
|
||||
|
26
network/lib/network/utils.lua
Normal file
26
network/lib/network/utils.lua
Normal file
@@ -0,0 +1,26 @@
|
||||
local utils = {}
|
||||
|
||||
function utils.checksum(data)
|
||||
local sum = 0;
|
||||
local count = #data
|
||||
local val, offset
|
||||
while count > 1 do
|
||||
val, offset = string.unpack('>H', data, offset)
|
||||
sum = sum + val
|
||||
count = count - 2;
|
||||
end
|
||||
|
||||
--Add left-over byte, if any
|
||||
if (count > 0) then
|
||||
sum = sum + string.unpack('>B', data, offset)
|
||||
end
|
||||
|
||||
--Fold 32-bit sum to 16 bits
|
||||
while (sum >> 16 ~= 0) do
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
end
|
||||
|
||||
return ~sum & 0xffff
|
||||
end
|
||||
|
||||
return utils
|
Reference in New Issue
Block a user