1
0
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:
2023-06-14 19:22:33 +02:00
parent 67ab7c9e04
commit 1c4461a16a
4 changed files with 132 additions and 40 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -9,4 +9,10 @@ ipv4Consts.PROTOCOLS = {
OSPF = 89, -- Open Shortest Path First
}
ipv4Consts.FLAGS = {
UNUSED = 4,
DF = 2,
MF = 1
}
return ipv4Consts

View 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