MODULE Network;
IMPORT SYSTEM, Machine, KernelLog, Plugins, Kernel, Objects, Modules;
CONST
MaxLinkAdrSize* = 8;
MaxPacketSize* = 1600;
MaxNofBuffers = 10000;
TypePointToPoint* = 0;
TypeEthernet* = 1;
LinkNotLinked* = 0;
LinkLinked* = 1;
LinkUnknown* = 2;
ChecksumIP* = 0;
ChecksumUDP* = 1;
ChecksumTCP* = 2;
MaxLoopbackPacketsPerMS = 500;
TYPE
LinkAdr* = ARRAY MaxLinkAdrSize OF CHAR;
Buffer* = POINTER TO RECORD
data*:ARRAY MaxPacketSize OF CHAR;
ofs*: LONGINT;
len*: LONGINT;
l3ofs*: LONGINT;
l4ofs*: LONGINT;
src*: LinkAdr;
calcChecksum*: SET;
int*: LONGINT;
set*: SET;
next*, prev*: Buffer;
nextFragment*: Buffer;
END;
TypeList = POINTER TO RECORD
next: TypeList;
type: LONGINT;
recList: ReceiverList;
END;
ReceiverList = POINTER TO RECORD
next: ReceiverList;
owner: ANY;
receiver: Receiver;
isPacketValid: IsPacketValid;
isPacketForSingleRec: IsPacketForSingleRec;
isPacketAccepted: IsPacketAccepted;
isForwardingOn: BOOLEAN;
END;
SendSnifferList = POINTER TO RECORD
next: SendSnifferList;
sniffer: SendSniffer;
END;
RecvSnifferList = POINTER TO RECORD
next: RecvSnifferList;
sniffer: ReceiveSniffer;
END;
LinkDevice* = OBJECT (Plugins.Plugin)
VAR
type-: LONGINT;
local*: LinkAdr;
broadcast*: LinkAdr;
mtu-: LONGINT;
adrSize*: LONGINT;
sendCount*, recvCount-: HUGEINT;
calcChecksum*: SET;
typeList: TypeList;
recList: ReceiverList;
sendSnifferList: SendSnifferList;
recvSnifferList: RecvSnifferList;
typeItem: TypeList;
recItem: ReceiverList;
sniffer: RecvSnifferList;
discard: BOOLEAN;
finalized: BOOLEAN;
upBufFirst, upBufLast: Buffer;
buf: Buffer;
bufSec: Buffer;
timer: Kernel.MilliTimer;
packetCount: LONGINT;
i: LONGINT;
PROCEDURE &Constr*(type, mtu, adrSize: LONGINT);
BEGIN
ASSERT((mtu >= 0) & (mtu <= MaxPacketSize));
ASSERT((adrSize >= 0) & (adrSize <= MaxLinkAdrSize));
IF type = TypeEthernet THEN
ASSERT(adrSize = 6);
END;
SELF.type := type;
SELF.mtu := mtu;
SELF.adrSize := adrSize;
SELF.sendCount := 0;
SELF.recvCount := 0;
SELF.calcChecksum := {};
typeList := NIL;
recList := NIL;
upBufFirst := NIL;
Kernel.SetTimer(timer, 2);
packetCount := 0;
finalized := FALSE;
sendSnifferList := NIL;
recvSnifferList := NIL;
END Constr;
PROCEDURE Finalize*(connected: BOOLEAN);
BEGIN {EXCLUSIVE}
ASSERT(~finalized);
finalized := TRUE;
END Finalize;
PROCEDURE Linked*(): LONGINT;
BEGIN
RETURN LinkUnknown;
END Linked;
PROCEDURE Send*(dst: LinkAdr; type: LONGINT; VAR l3hdr, l4hdr, data: ARRAY OF CHAR; h3len, h4len, dofs, dlen: LONGINT; loopback: BOOLEAN);
VAR
sniffer: SendSnifferList;
discard: BOOLEAN;
BEGIN
ASSERT(~finalized);
discard := FALSE;
sniffer := sendSnifferList;
WHILE sniffer # NIL DO
discard := discard OR sniffer^.sniffer(SELF, dst, type, l3hdr, l4hdr, data, h3len, h4len, dofs, dlen);
sniffer := sniffer^.next;
END;
IF ~discard THEN
IF loopback THEN
Loopback(dst, type, l3hdr, l4hdr, data, h3len, h4len, dofs, dlen);
ELSE
DoSend(dst, type, l3hdr, l4hdr, data, h3len, h4len, dofs, dlen);
END;
INC(sendCount, dlen + h3len + h4len);
END;
END Send;
PROCEDURE DoSend*(dst: LinkAdr; type: LONGINT; VAR l3hdr, l4hdr, data: ARRAY OF CHAR; h3len, h4len, dofs, dlen: LONGINT);
BEGIN
HALT(301);
END DoSend;
PROCEDURE Loopback(dst: LinkAdr; type: LONGINT; VAR l3hdr, l4hdr, data: ARRAY OF CHAR; h3len, h4len, dofs, dlen: LONGINT);
VAR buf: Buffer;
BEGIN
IF packetCount >= MaxLoopbackPacketsPerMS THEN
WHILE ~Kernel.Expired(timer) DO
Objects.Yield();
END;
Kernel.SetTimer(timer, 2);
packetCount := 0;
END;
buf := GetNewBuffer();
IF buf # NIL THEN
buf.l3ofs := 0;
buf.l4ofs := 0;
buf.ofs := 0;
buf.len := 0;
buf.src := dst;
buf.calcChecksum := {ChecksumIP, ChecksumUDP, ChecksumTCP};
Copy(l3hdr, buf.data, 0, buf.len, h3len);
INC(buf.len, h3len);
Copy(l4hdr, buf.data, 0, buf.len, h4len);
INC(buf.len, h4len);
Copy(data, buf.data, dofs, buf.len, dlen);
INC(buf.len, dlen);
QueueBuffer(buf, type);
Machine.AtomicInc(packetCount)
ELSE
END
END Loopback;
PROCEDURE InstallReceiver*(owner: ANY;
type: LONGINT;
receiver: Receiver;
isPacketValid: IsPacketValid;
isPacketForSingleRec: IsPacketForSingleRec;
isPacketAccepted: IsPacketAccepted;
isForwardingOn: BOOLEAN);
VAR
typeItem: TypeList;
recItem: ReceiverList;
BEGIN {EXCLUSIVE}
ASSERT(owner # NIL);
ASSERT(~finalized);
ASSERT(receiver # NIL);
ASSERT(isPacketValid # NIL);
ASSERT(isPacketForSingleRec # NIL);
ASSERT(isPacketAccepted # NIL);
typeItem := typeList;
WHILE (typeItem # NIL) & (typeItem^.type # type) DO
typeItem := typeItem^.next;
END;
IF typeItem = NIL THEN
NEW(typeItem);
typeItem^.next := typeList;
typeItem^.type := type;
typeItem^.recList := NIL;
typeList := typeItem;
END;
NEW(recItem);
recItem^.owner := owner;
recItem^.receiver := receiver;
recItem^.isPacketValid := isPacketValid;
recItem^.isPacketForSingleRec := isPacketForSingleRec;
recItem^.isPacketAccepted := isPacketAccepted;
recItem^.isForwardingOn := isForwardingOn;
recItem^.next := typeItem^.recList;
typeItem^.recList := recItem;
END InstallReceiver;
PROCEDURE RemoveReceiver*(owner: ANY; type: LONGINT);
VAR
typeItem: TypeList;
recItem: ReceiverList;
typeItemDel: TypeList;
BEGIN {EXCLUSIVE}
ASSERT(owner # NIL);
ASSERT(~finalized);
typeItem := typeList;
WHILE (typeItem # NIL) & (typeItem^.type # type) DO
typeItem := typeItem^.next;
END;
IF typeItem # NIL THEN
recItem := typeItem^.recList;
IF (recItem # NIL) & (recItem^.owner = owner) THEN
typeItem^.recList := recItem^.next;
ELSE
WHILE (recItem^.next # NIL) & (recItem^.next^.owner # owner) DO
recItem := recItem^.next;
END;
IF recItem^.next # NIL THEN
recItem^.next := recItem^.next^.next;
END;
END;
IF typeItem^.recList = NIL THEN
typeItemDel := typeItem;
typeItem := typeList;
IF (typeItem = typeItemDel) THEN
typeList := typeItem^.next;
ELSE
WHILE (typeItem^.next # typeItemDel) DO
typeItem := typeItem^.next;
END;
typeItem^.next := typeItem^.next^.next;
END;
END;
END;
END RemoveReceiver;
PROCEDURE InstallSendSniffer*(s: SendSniffer);
VAR item: SendSnifferList;
BEGIN {EXCLUSIVE}
ASSERT(~finalized);
item := sendSnifferList;
WHILE (item # NIL) & (item^.sniffer # s) DO
item := item^.next;
END;
IF item # NIL THEN
ELSE
NEW(item);
item^.sniffer := s;
item^.next := sendSnifferList;
sendSnifferList := item;
END;
END InstallSendSniffer;
PROCEDURE RemoveSendSniffer*(s: SendSniffer);
VAR item: SendSnifferList;
BEGIN {EXCLUSIVE}
ASSERT(~finalized);
IF sendSnifferList = NIL THEN
ELSIF sendSnifferList^.sniffer = s THEN
sendSnifferList := sendSnifferList^.next;
ELSE
item := sendSnifferList;
WHILE (item^.next # NIL) & (item^.next^.sniffer # s) DO
item := item^.next;
END;
IF item^.next # NIL THEN
item^.next := item^.next^.next;
ELSE
END;
END;
END RemoveSendSniffer;
PROCEDURE InstallReceiveSniffer*(s: ReceiveSniffer);
VAR item: RecvSnifferList;
BEGIN {EXCLUSIVE}
ASSERT(~finalized);
item := recvSnifferList;
WHILE (item # NIL) & (item^.sniffer # s) DO
item := item^.next;
END;
IF item # NIL THEN
ELSE
NEW(item);
item^.sniffer := s;
item^.next := recvSnifferList;
recvSnifferList := item;
END;
END InstallReceiveSniffer;
PROCEDURE RemoveReceiveSniffer*(s: ReceiveSniffer);
VAR item: RecvSnifferList;
BEGIN {EXCLUSIVE}
ASSERT(~finalized);
IF recvSnifferList = NIL THEN
ELSIF recvSnifferList^.sniffer = s THEN
recvSnifferList := recvSnifferList^.next;
ELSE
item := recvSnifferList;
WHILE (item^.next # NIL) & (item^.next^.sniffer # s) DO
item := item^.next;
END;
IF item^.next # NIL THEN
item^.next := item^.next^.next;
ELSE
END;
END;
END RemoveReceiveSniffer;
PROCEDURE QueueBuffer*(buf: Buffer; type: LONGINT);
BEGIN {EXCLUSIVE}
ASSERT(buf # NIL);
buf.int := type;
buf.next := NIL;
IF upBufFirst = NIL THEN
upBufFirst := buf;
ELSE
upBufLast.next := buf;
END;
upBufLast := buf;
END QueueBuffer;
BEGIN {ACTIVE, PRIORITY(Objects.High)}
(* can run concurrently with SetReceiver, QueueBuffer, InstallReceiverSniffer and RemoveReceiverSniffer *)
LOOP
BEGIN {EXCLUSIVE}
AWAIT((upBufFirst # NIL) OR finalized);
IF (upBufFirst = NIL) & finalized THEN
(* terminate process after all buffer upcalls are done *)
EXIT;
END;
buf := upBufFirst;
upBufFirst := upBufFirst.next;
END;
INC(recvCount, buf.len);
discard := FALSE;
sniffer := recvSnifferList;
WHILE sniffer # NIL DO
(* call sniffer *)
discard := discard OR sniffer^.sniffer(SELF, buf.int, buf);
sniffer := sniffer^.next;
END;
IF ~discard THEN
(* search for type *)
typeItem := typeList;
WHILE (typeItem # NIL) & (typeItem^.type # buf.int) DO
typeItem := typeItem^.next;
END;
IF typeItem # NIL THEN
(* type item found check if packet is valid *)
recItem := typeItem^.recList;
IF (recItem # NIL) & (recItem^.isPacketValid(buf)) THEN
IF recItem^.next # NIL THEN
(* multiple receivers installed *)
IF ~(recItem^.isPacketForSingleRec(buf)) THEN
(* multiple receivers copy buffer *)
WHILE recItem^.next # NIL DO
bufSec := GetNewBuffer();
(* copy buffer *)
FOR i := 0 TO MaxPacketSize - 1 DO
bufSec^.data[i] := buf^.data[i];
END;
bufSec^.ofs := buf^.ofs;
bufSec^.len := buf^.len;
bufSec^.l3ofs := buf^.l3ofs;
bufSec^.l4ofs := buf^.l4ofs;
bufSec^.src := buf^.src;
bufSec^.calcChecksum := buf^.calcChecksum;
bufSec^.int := buf^.int;
bufSec^.set := buf^.set;
(* deliver copied buffer to interface *)
recItem^.receiver(SELF, typeItem^.type, bufSec);
recItem := recItem^.next;
END;
(* deliver original buffer *)
recItem^.receiver(SELF, typeItem^.type, buf);
ELSE
(* search for a single receiver *)
WHILE (recItem # NIL) & ~(recItem^.isPacketAccepted(buf)) DO
recItem := recItem^.next;
END;
IF recItem # NIL THEN
(* deliver buffer *)
recItem^.receiver(SELF, typeItem^.type, buf);
ELSE
(* Packet is not accepted ip ipforwarding is turned on deliver it *)
recItem := typeItem^.recList;
IF recItem^.isForwardingOn THEN
(* deliver buffer *)
recItem^.receiver(SELF, typeItem^.type, buf);
ELSE
discard := TRUE;
END;
END;
END;
ELSE
(* single receiver deliver buffer directly *)
IF recItem^.isPacketAccepted(buf) OR ~(recItem^.isPacketForSingleRec(buf)) THEN
recItem^.receiver(SELF, typeItem^.type, buf);
ELSE
(* Packet is not accepted ip ipforwarding is turned on deliver it *)
IF recItem^.isForwardingOn THEN
(* deliver buffer *)
recItem^.receiver(SELF, typeItem^.type, buf);
ELSE
discard := TRUE;
END;
END;
END;
ELSE
discard := TRUE;
END;
ELSE
discard := TRUE;
END;
END;
IF discard THEN
(* discard packet and return buffer *)
ReturnBuffer(buf);
END;
END;
END LinkDevice;
TYPE
Receiver* = PROCEDURE {DELEGATE} (dev: LinkDevice; type: LONGINT; buffer: Buffer);
IsPacketForSingleRec* = PROCEDURE {DELEGATE} (buffer: Buffer): BOOLEAN;
IsPacketAccepted* = PROCEDURE {DELEGATE} (buffer: Buffer): BOOLEAN;
IsPacketValid* = PROCEDURE {DELEGATE} (VAR buffer: Buffer): BOOLEAN;
SendSniffer* = PROCEDURE {DELEGATE} (dev: LinkDevice; VAR dst: LinkAdr; VAR type: LONGINT; VAR l3hdr, l4hdr, data: ARRAY OF CHAR; VAR h3len, h4len, dofs, dlen: LONGINT): BOOLEAN;
ReceiveSniffer* = PROCEDURE {DELEGATE} (dev: LinkDevice; VAR type: LONGINT; buffer: Buffer): BOOLEAN;
VAR
registry*: Plugins.Registry;
nofBuf: LONGINT;
nofFreeBuf: LONGINT;
freeBufList: Buffer;
PROCEDURE GetNewBuffer*(): Buffer;
VAR item: Buffer;
BEGIN {EXCLUSIVE}
IF freeBufList # NIL THEN
item := freeBufList;
freeBufList := freeBufList.next;
Machine.AtomicAdd(nofFreeBuf, -1);
ELSIF nofBuf < MaxNofBuffers THEN
NEW(item);
Machine.AtomicInc(nofBuf);
ELSE
item := NIL;
END;
RETURN item;
END GetNewBuffer;
PROCEDURE ReturnBuffer*(buf: Buffer);
VAR
oldBuffer: Buffer;
BEGIN {EXCLUSIVE}
oldBuffer := buf;
WHILE buf # NIL DO
buf.next := freeBufList;
freeBufList := buf;
Machine.AtomicInc(nofFreeBuf);
oldBuffer := buf;
buf := buf.nextFragment;
oldBuffer.nextFragment := NIL;
END;
END ReturnBuffer;
PROCEDURE Finalize(p: Plugins.Plugin);
BEGIN
p(LinkDevice).Finalize(TRUE);
END Finalize;
PROCEDURE Equal*(VAR buf1, buf2: ARRAY OF CHAR; ofs1, ofs2, n: LONGINT): BOOLEAN;
BEGIN
WHILE (n > 0) & (buf1[ofs1] = buf2[ofs2]) DO INC(ofs1); INC(ofs2); DEC(n) END;
RETURN n <= 0
END Equal;
PROCEDURE Put4*(VAR buf: ARRAY OF CHAR; ofs, val: LONGINT);
CODE {SYSTEM.AMD64}
MOV EAX, [RBP + val]
MOV EBX, [RBP + ofs]
MOV RCX, [RBP + buf]
MOV [RCX + EBX], EAX
END Put4;
PROCEDURE Put2*(VAR buf: ARRAY OF CHAR; ofs, val: LONGINT);
CODE {SYSTEM.AMD64}
MOV EAX, [RBP + val]
MOV EBX, [RBP + ofs]
MOV RCX, [RBP + buf]
MOV [RCX + EBX], AX
END Put2;
PROCEDURE Get4*(VAR buf: ARRAY OF CHAR; ofs: LONGINT): LONGINT;
CODE {SYSTEM.AMD64}
MOV EBX, [RBP + ofs]
MOV RCX, [RBP + buf]
MOV EAX, [RCX + EBX]
END Get4;
PROCEDURE Get2*(VAR buf: ARRAY OF CHAR; ofs: LONGINT): LONGINT;
CODE {SYSTEM.AMD64}
MOV EBX, [RBP + ofs]
MOV RCX, [RBP + buf]
XOR EAX, EAX
MOV AX, [RCX + EBX]
END Get2;
PROCEDURE PutNet4*(VAR buf: ARRAY OF CHAR; ofs, val: LONGINT);
BEGIN
buf[ofs] := CHR (val DIV 1000000H);
buf[ofs+1] := CHR (val DIV 10000H);
buf[ofs+2] := CHR (val DIV 100H);
buf[ofs+3] := CHR (val);
END PutNet4;
PROCEDURE PutNet2*(VAR buf: ARRAY OF CHAR; ofs, val: LONGINT);
BEGIN
buf[ofs] := CHR (val DIV 100H);
buf[ofs+1] := CHR (val);
END PutNet2;
PROCEDURE GetNet4*(VAR buf: ARRAY OF CHAR; ofs: LONGINT): LONGINT;
VAR val: LONGINT;
BEGIN
val := ORD(buf[ofs]);
val := val * 100H + ORD(buf[ofs+1]);
val := val * 100H + ORD(buf[ofs+2]);
RETURN val * 100H + ORD(buf[ofs+3]);
END GetNet4;
PROCEDURE GetNet2*(VAR buf: ARRAY OF CHAR; ofs: LONGINT): LONGINT;
VAR val: LONGINT;
BEGIN
val := ORD(buf[ofs]);
RETURN val * 100H + ORD(buf[ofs+1]);
END GetNet2;
PROCEDURE LinkAdrToStr*(VAR adr: LinkAdr; size: LONGINT; VAR s: ARRAY OF CHAR);
VAR
i, j: LONGINT;
hex: ARRAY 17 OF CHAR;
BEGIN
ASSERT(LEN(s) >= size*3);
hex := "0123456789ABCDEF";
i := 0;
FOR j := 0 TO size-1 DO
s[i] := hex[ORD(adr[j]) DIV 10H MOD 10H]; INC(i);
s[i] := hex[ORD(adr[j]) MOD 10H]; INC(i);
IF j = size-1 THEN s[i] := 0X ELSE s[i] := ":" END;
INC(i);
END;
END LinkAdrToStr;
PROCEDURE OutLinkAdr*(VAR adr: LinkAdr; size: LONGINT);
VAR s: ARRAY MaxLinkAdrSize*3 OF CHAR;
BEGIN
LinkAdrToStr(adr, size, s);
KernelLog.String(s);
END OutLinkAdr;
PROCEDURE LinkAdrsEqual*(adr1: LinkAdr; adr2: LinkAdr): BOOLEAN;
VAR
i: LONGINT;
adrsEqual: BOOLEAN;
BEGIN
adrsEqual := TRUE;
i := 0;
WHILE (i<8) & (adr1[i] = adr2[i]) DO
INC(i);
END;
IF i < 8 THEN
adrsEqual := FALSE;
END;
RETURN adrsEqual;
END LinkAdrsEqual;
PROCEDURE Copy*(CONST from: ARRAY OF CHAR; VAR to: ARRAY OF CHAR; fofs, tofs, len: LONGINT);
BEGIN
IF len > 0 THEN
ASSERT((fofs+len <= LEN(from)) & (tofs+len <= LEN(to)));
SYSTEM.MOVE(SYSTEM.ADR(from[fofs]), SYSTEM.ADR(to[tofs]), len);
END;
END Copy;
PROCEDURE Cleanup;
BEGIN
registry.Enumerate(Finalize);
Plugins.main.Remove(registry)
END Cleanup;
BEGIN
nofBuf := 0;
nofFreeBuf := 0;
NEW(registry, "Network", "Network interface drivers");
Modules.InstallTermHandler(Cleanup);
END Network.
(*
History:
10.10.2003 mvt Complete redesign and additional implementation of buffer handling and upcall mechanism
17.10.2003 mvt Changed the way of initialization and finalization (now only Constr/Finalize)
21.10.2003 mvt Changed SetReceiver to InstallReceiver and RemoveReceiver
15.11.2003 mvt Changed buffering to work with EXCLUSIVE sections instead of using locking and a semaphore.
16.11.2003 mvt Added support for checksum calclulation by the device.
25.11.2003 mvt Added l3ofs and l4ofs to Buffer type.
17.12.2003 mvt Changed variable "linked" to method "Linked".
02.05.2005 eb Supports multiple interfaces per device.
*)
(**
How to use the module:
The module is loaded as soon as it is used first. It needn't to be loaded explicitly at startup. It can also be unloaded an reloaded without reboot.
How to use a driver:
Network driver objects in Bluebottle are extensions of the Network.LinkDevice object. All loaded instances of network driver objects are registered in the registry Network.registry. To obtain access to a network driver, use the Get, Await or GetAll methods of this registry.
Example:
VAR dev: Network.LinkDevice;
dev := Network.registry.Get(""); (* if no name is specified, first device (or NIL) is returned *)
The Send method of LinkDevice is used to send a packet. The dst parameter specifies the link destination address (e.g., a 6-byte ethernet MAC address for an ethernet device). The type parameter specifies the link-layer protocol type (e.g. 800H when sending IP over ethernet). The source address of the packet is automatically generated by the device, if necessary.
For reasons of reducing buffer copying between network layers, the method allows 3 buffers to be passed:
The l3hdr, l4hdr, data, h3len, h4len, dofs and dlen fields specify 3 buffers:
One buffer for a layer 3 header, one for a layer 4 header and one for the payload of the packet. The buffers don't have to be filled like this. They are simply concatenated to one frame by the device driver. Therefore, each of them is allowed to be empty.
The layer 3 header is stored in: l3hdr[0..h3len-1]
The layer 4 header is stored in: l4hdr[0..h4len-1]
The payload is stored in data[dofs..dofs+dlen-1]
Example:
CONST
Type = 05555H; (* link-layer protocol type *)
VAR
dlen: LONGINT;
l3hdr: ARRAY HdrLen OF CHAR;
data: ARRAY MaxDataLen OF CHAR;
(* - l3hdr[0..HdrLen-1] contains the layer 3 packet header.
- data[0..dlen-1] contains the packet data.
- there is no layer 4 header in this example, i.e. an empty buffer is passed (len=0)
*)
dev.Send(dev.broadcast, Type, l3hdr, l3hdr, data, HdrLen, 0, 0, dlen); (* send a broadcast packet *)
Packet receiving is driven by the driver object. A receiver interested in a specific type of packet registers itself using the InstallReceiver method. The type parameter specifies the link-layer protocol type, which must be unique. There can only be one receiver installed per type. When a packet arrives, the driver object looks at the protocol type and calls the specific receiver (if any is installed for this type).
Example:
PROCEDURE Receiver(dev: Network.LinkDevice; type: LONGINT; buffer: Network.Buffer);
BEGIN
ASSERT(type = Type);
CheckAdr(buffer.src); (* some link layer source address checks *)
IF ~(ChecksumForThisProtocol IN buffer.calcChecksum) THEN
VerifyChecksum(buffer);
END;
ExamineLayer3Header(buffer.data, buffer.ofs, Layer3HeaderSize);
ProcessPayload(buffer.data, buffer.ofs+Layer3HeaderSize, buffer.len-Header3LayerSize);
(* MANDATORY!!
Buffer must be returned here! - or at higher layers, if the buffer is passed there! *)
Network.ReturnBuffer(buffer);
END Receiver;
dev.InstallReceiver(Type, Receiver); (* install the receiver *)
When passing the buffer to a higher layer (e.g. layer 4), the field "l3ofs" should be set in order to enable the higher layer protocol to access this layer's header (required by ICMP).
The same is valid for the field "l4ofs" when passing the buffer to a higher layer than 4.
The "type" field of a LinkDevice specifies what kind of device it is. Currently, two options (constants) are available:
- TypePointToPoint: dev is a point-to-point link (device), e.g. PPP
- TypeEthernet: dev is an ethernet device
For point-to-point links, the following rules are met:
In constructor, the "local" and "broadcast" parameters are ignored.
If it is not possible to transmit the layer 3 type of packet, the type is set to 0 for the received packet.
If the field "adrSize" is 0, the dst address parameter in Send() is ignored and the src address parameter in the Receiver is not defined.
If "adrSize" is > 0, the dst address passed in Send() is transmitted and presented as src address for the received packet.
The "local" and "broadcast" fields of the object specify the link-level address of the device, and the broadcast address, respectively. If needed, they have to be set during device initialization.
The mtu field specifies the largest allowed packet size in bytes, excluding layer 2 headers and trailers (e.g. 1500 for ethernet).
The "sendCount" and "recvCount" fields show the number of data bytes sent and received by the device since the driver object was loaded.
How to implement a driver:
IMPORTANT:
- Read first "How to use a driver"!
- Read comments of methods you override!
A network driver object is implemented by extending the LinkDevice object. At least the "DoSend" method has to be overridden with a concrete implementation. Normally, you will also override the constructor, the destructor (method "Finalize") and the method "Linked".
CAUTION:
If you override the constructor or destructor:
- At the beginning of the overriding constructor, the overridden constructor has to be called!
- At the end of the overriding destructor, the overridden destructor has to be called!
NOTE:
The device has to be registered and deregistered as a Plugin in Network.registry by the device driver as follows:
Add the device to the registry after it is ready to send/receive data!
Remove the device from the registry before you begin to deinitialize the device!
Normally, the device driver will have to install an interrupt handler for receiving packets. This handler must be installed in Objects. Interrupts registered in Machine are not allowed! (because they are not allowed to call QueueBuffer due to its EXCLUSIVE section)
If you use interrupts:
- do the minimum operations required for receiving and queueing a packet!
- return immediately after having done the operations!
Receiving and queueing packets is done like this:
When you are notified of an incomming network packet (normally by an interrupt), get a new buffer by calling:
buf := Network.GetNewBuffer();
If buf = NIL, the packet has to be discarded because all buffers are currently in use and the maximum amount of buffers (MaxNofBuffers) is exceeded.
If buf # NIL (the normal case), read the packet data into buf.data and set buf.ofs and buf.len accordingly.
The buffer is not initialized in any way.
If the device supports DMA, you could try to get the buffers earlier and pass their physical addesses to the device. With this you can save one packet copying operation!
In addition to the fields "data", "ofs", "len" of "buffer", it is mandatory to set the fields "src" and "calcChecksum" correctly. "src" is the link layer source address and "calcChecksum" is the set of checksums already verified by the device (normally {}).
As soon as the buffer contains the whole packet data, it is passed to the upper layers by calling:
QueueBuffer(buf, type);
Where "type" is the layer 3 type of the packet.
See TestNet.Mod (SendBroadcast, SendTest and Receiver) and IP.Mod (ARPRequest and ARPInput) for an example of packet sending and receiving. See Ethernet3Com90x.Mod for an example of an ethernet driver object and Loopback.Mod for a simple point-to-point link implementation.
*)