123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044 |
- MODULE EnetBase;
- (**
- AUTHOR: Alexey Morozov, HighDim GmbH, 2015
- PURPOSE: Ethernet networking stack
- *)
- IMPORT
- S := SYSTEM, EnetEnvironment, EnetUtils, Trace := EnetTrace, EnetTiming;
- TYPE
- Int8* = SHORTINT;
- Int16* = INTEGER;
- Int32* = LONGINT;
- Int* = LONGINT; (** system signed integer type *)
- UInt* = ADDRESS; (** system unsigned integer type *)
- UnalignedInt32 = EnetUtils.UnalignedInt32;
- CONST
- ThreadSafe* = FALSE; (** set to TRUE to enable thread safety features *)
- LittleEndianSystem* = TRUE; (** TRUE if the system has little endian data ordering *)
- (** Link speed values in Mbps *)
- LinkSpeedAuto* = "Auto";
- Mbps10* = "10";
- Mbps100* = "100";
- Mbps1000* = "1000";
- MaxNumEthFrameHandlers* = 32; (** maximal number of plugable custom Ethernet frame handlers *)
- (** Basic Ethernet frame types *)
- EtherTypeIpv4* = 0x0008;
- EtherTypeIpv6* = 0xDD86;
- EtherTypeArp* = 0x0608;
- (**
- Protocols on top of IP (Level 3)
- *)
- ProtoIcmp* = 0x01; (** ICMP *)
- ProtoTcp* = 0x06; (** TCP *)
- ProtoUdp* = 0x11; (** UDP *)
- (**
- IP-specific constants
- *)
- IpDiffServ* = 0; (* default value of "Differentiated Services" field in an IP packet *)
- IpEcn* = 0; (** default value of "Explicit Congestion Notification" field in an IP packet *)
- IpTtl* = 255; (** time to live for an IP packet *)
- (**
- Packet sending flags
- *)
- FlagNoDataCopy* = 0; (** do not copy user-provided data for sending a packet, the data will be used by reference; use completion handlers to assure that the data is not referenced anymore *)
- FlagNoFlushDCache* = 1; (** avoid flushing data cache for the user-provided data (used for platforms with explicit cache manipulation e.g. ARM) *)
- (**
- Packet handling flags
- *)
- FlagIpv6* = 0; (** passed to the IP packet handler to indicate IP version 6 *)
- (**
- Error codes
- *)
- OpInProgress* = -1; (** operation is still in progress *)
- ErrInvalidValue* = 1; (** a parameter has invalid value *)
- ErrTimeoutExpired* = 2; (** timeout has expired *)
- ErrCannotDoWhenActive* = 3; (** operation cannot be performed when the interface/device is active *)
- ErrNotActive* = 4; (** operation cannot be performed when the interface/device is not active *)
- ErrNoIntfFound* = 5; (** no interface found to perform a given operation *)
- ErrUnresolvedAddr* = 6; (** address not resolved error *)
- ErrOutOfResources* = 7; (** out of resources *)
- ErrOutOfBounds* = 8; (** out of bounds *)
- ErrAlreadyExists* = 9; (** an object already exists *)
- (**
- PHY-specific error codes
- *)
- ErrMdioBusy* = 10; (** MDIO interface is busy *)
- ErrPhyNotDetected* = 11; (** PHY device is not detected *)
- ErrSpeedAutonegoFailure* = 12; (** a failure to autonegotiate the link speed *)
- (**
- Link device specific error codes
- *)
- ErrRxPacketPoolEmpty* = 13; (** RX packet pool got empty *)
- ErrRxPacketPoolFull* = 14; (** RX packet pool got full *)
- ErrRecvPacketFifoFull* = 15; (** received packet FIFO got full *)
- ErrTxPacketPoolEmpty* = 16; (** TX packet pool got empty *)
- EnableTrace * = FALSE;
- TYPE
- (**
- MAC address
- *)
- MacAddr* = RECORD
- addr*: ARRAY 6 OF Int8;
- END;
- IpAddr* = RECORD
- addr*: ARRAY 4 OF Int32;
- ver*: Int8;
- END;
- (** Ethernet frame header (14 bytes) *)
- EthFrameHdr* = RECORD
- dstMacAddr*: MacAddr; (** destination MAC address *)
- srcMacAddr*: MacAddr; (** source MAC address *)
- etherType*{ALIGNED(1)}: Int16; (** type of the Ethernet frame; value >= 1536 corresponds to a Ethernet II frame *)
- END;
- (**
- IP version 4 packet header (20 bytes)
- *)
- Ipv4Hdr* = RECORD
- verAndIhl*: Int8; (** 4-bit version field (4 for For IPv4) and 4-bit value of Internet Header Length (IHL) *)
- dscpAndEcn*: Int8; (** 6-bit Differentiated Services Code Point (DSCP) and 2-bit Explicit Congestion Notification (ECN) *)
- length*{ALIGNED(1)}: Int16; (** 16-bit size of the entire packet (fragment), including header and data, in bytes *)
- fragmentId*{ALIGNED(1)}: Int16; (** identification field and is primarily used for uniquely identifying the group of fragments of a single IP datagram *)
- flagsAndFragmentOffs*{ALIGNED(1)}: Int16; (** 3-bit flags field used to control or identify fragments and 13-bit fragment offset field, measured in units of eight-byte blocks (64 bits), and specifies the offset of a particular fragment relative to the beginning of the original unfragmented IP datagram *)
- ttl*: Int8; (** Time To Live (TTL) *)
- protocol*: Int8; (** defines the protocol used in the data portion of the IP datagram *)
- checksum*{ALIGNED(1)}: Int16; (** header checksum *)
- srcIpAddr*{ALIGNED(1)}: UnalignedInt32; (** source IPv4 address *)
- dstIpAddr*{ALIGNED(1)}: UnalignedInt32; (** destination IPv4 address *)
- END;
- (**
- IP version 6 packet header (XY bytes)
- *)
- Ipv6Hdr* = RECORD
- protocol*: Int8; (** defines the protocol used in the data portion of the IP datagram *)
- srcIpAddr*{ALIGNED(1)}: ARRAY 4 OF UnalignedInt32; (** source IPv6 address *)
- dstIpAddr*{ALIGNED(1)}: ARRAY 4 OF UnalignedInt32; (** destination IPv6 address *)
- END;
- (**
- Address Resolution Protocol (ARP) packet header (28 bytes)
- *)
- ArpHdr* = RECORD
- hwType*{ALIGNED(1)}: Int16;
- protoType*{ALIGNED(1)}: Int16;
- hwAddrLen*: Int8;
- protoAddrLen*: Int8;
- operation*{ALIGNED(1)}: Int16;
- srcMacAddr*: MacAddr;
- srcIpAddr*{ALIGNED(1)}: UnalignedInt32;
- dstMacAddr*: MacAddr;
- dstIpAddr*{ALIGNED(1)}: UnalignedInt32;
- END;
- (**
- Internet Control Message Protocol (ICMP) packet basic part of the header (4 bytes)
- *)
- IcmpHdr0* = RECORD
- type*: Int8; (** message type *)
- code*: Int8; (** message subtype *)
- checksum*{ALIGNED(1)}: Int16; (** checksum computed from the header and data *)
- END;
- (**
- ICMP header (8 bytes)
- *)
- IcmpHdr* = RECORD(IcmpHdr0)
- restOfHdr*{ALIGNED(1)}: UnalignedInt32; (** rest of the header (content depends on the value of "type" field) *)
- END;
- (**
- ICMP echo request/reply header
- *)
- IcmpEchoHdr* = RECORD(IcmpHdr0)
- id*{ALIGNED(1)}: Int16; (** echo message identifier *)
- seq*{ALIGNED(1)}: Int16; (** echo message sequence number *)
- END;
- (**
- User Datagram Protocol header (8 bytes)
- *)
- UdpHdr* = RECORD
- srcPort*{ALIGNED(1)}: Int16; (** sender's port number when meaningful and should be assumed to be the port to reply to if needed *)
- dstPort*{ALIGNED(1)}: Int16; (** receiver's port number *)
- length*{ALIGNED(1)}: Int16; (** length of the UDP header and UDP data, in bytes *)
- checksum*{ALIGNED(1)}: Int16; (** checksum computed from the header and data *)
- END;
- (** Transmit Control Protocol header (XY bytes) *)
- TcpHdr* = RECORD
- END;
- (**
- Network packet
- *)
- Packet* = POINTER TO PacketDesc;
- PacketDesc* = RECORD
- data*: POINTER TO ARRAY OF CHAR; (** packet data *)
- dataOffs*: Int; (** data array offset (set up by the corresponding link device) *)
- dataLen*: Int; (** packet data length in bytes *)
- (* Level 2 *)
- ethFrameHdr*: POINTER{UNSAFE,UNTRACED} TO EthFrameHdr;
- (* Level 3 *)
- ipv4Hdr*: POINTER{UNSAFE,UNTRACED} TO Ipv4Hdr;
- ipv6Hdr*: POINTER{UNSAFE,UNTRACED} TO Ipv6Hdr;
- arpHdr*: POINTER{UNSAFE,UNTRACED} TO ArpHdr;
- (* Level 4 *)
- icmpHdr*: POINTER{UNSAFE,UNTRACED} TO IcmpHdr;
- udpHdr*: POINTER{UNSAFE,UNTRACED} TO UdpHdr;
- tcpHdr*: POINTER{UNSAFE,UNTRACED} TO TcpHdr;
- payloadOffs*: Int; (** payload offset for the top level protocol, relative to dataOffs *)
- ownedByDev*: BOOLEAN; (** TRUE if the packet is owned by a link device and cannot be used by the user *)
- ownedByUser*: BOOLEAN; (** TRUE if the packet is owned by the user *)
- ownerPool*: PacketFifo; (** owner pool of the packet; used for returning received packets *)
- intf*: Interface;
- END;
- (** procedure type for handling packets received from an interface *)
- PacketHandler* = PROCEDURE(intf: Interface; packet: Packet; flags: SET);
- (**
- Ethernet frame handler descriptor
- *)
- EthFrameHandlerDesc* = RECORD
- etherType*: Int16;
- packetHandler*: PacketHandler;
- END;
- (** IP address cache *)
- IpAddrCache* = POINTER TO IpAddrCacheDesc;
- IpAddrCacheDesc* = RECORD
- cleanCache*: PROCEDURE{DELEGATE}(cache: IpAddrCache; cleanStatic: BOOLEAN);
- addStaticEntry*: PROCEDURE{DELEGATE}(cache: IpAddrCache; CONST ipAddr: IpAddr; CONST macAddr: MacAddr; VAR res: Int): BOOLEAN;
- enumerateEntries*: PROCEDURE{DELEGATE}(cache: IpAddrCache; enumerator: PROCEDURE{DELEGATE}(entry: IpAddrCacheEntry));
- acquireWrite*, acquireRead*: PROCEDURE{DELEGATE}();
- releaseWrite*, releaseRead*: PROCEDURE{DELEGATE}();
- intf*: Interface;
- END;
- (** an entry of an IP address cache *)
- IpAddrCacheEntry* = POINTER TO IpAddrCacheEntryDesc;
- IpAddrCacheEntryDesc* = RECORD
- ipAddr*: IpAddr;
- macAddr*: MacAddr;
- END;
- (** IP address resolution procedure *)
- IpAddrResolver* = PROCEDURE{DELEGATE}(intf: Interface; CONST ipAddr: IpAddr; VAR macAddr: MacAddr; completionHandler: TaskHandler; VAR res: Int): BOOLEAN;
- (**
- Network interface
- *)
- Interface* = POINTER TO InterfaceDesc;
- InterfaceDesc* = RECORD
- start*, stop*, reset*, finalize*: PROCEDURE{DELEGATE}(intf: Interface; VAR res: Int): BOOLEAN;
- (**
- IPv4 network settings
- *)
- ipv4Addr*: IpAddr; (** IPv4 address of the interface *)
- ipv4SubnetMask*: IpAddr; (** IPv4 subnet mask *)
- ipv4Prefix*: Int32; (** IPv4 address prefix *)
- ipv4Gateway*: IpAddr; (** IPv4 default gateway address *)
- (**
- IPv6 network settings
- *)
- ipv6Addr*: IpAddr; (** IPv6 address of the interface *)
- ipv6SubnetMask*: IpAddr; (** IPv6 subnetwork mask *)
- ipv6Prefix*: IpAddr; (** IPv6 address prefix *)
- ipv6Gateway*: IpAddr; (** IPv6 default gateway address *)
- dev*: LinkDevice; (** Link device *)
- (** plugable LAN IPv4 address resolution functionality *)
- ipv4AddrCache*: IpAddrCache; (** IPv4 address resolution cache *)
- ipv4AddrResolve*: IpAddrResolver;
- (** plugable LAN IPv6 address resolution functionality *)
- ipv6AddrCache*: IpAddrCache; (** IPv6 address resolution cache *)
- ipv6AddrResolve*: IpAddrResolver;
- (* interface-specific periodic and non-periodic tasks *)
- nonPeriodicTasks, periodicTasks: TaskHandler;
- acquireTasks*, releaseTasks*: PROCEDURE{DELEGATE}();
- ethFrameHandlers: ARRAY MaxNumEthFrameHandlers OF EthFrameHandlerDesc;
- numEthFrameHandlers: Int;
- ipPacketHandlers: ARRAY 256 OF PacketHandler;
- (* packet handlers for basic protocols *)
- arpHandler: PacketHandler;
- icmpHandler: PacketHandler;
- udpHandler: PacketHandler;
- tcpHandler: PacketHandler;
- END;
- (**
- Ethernet link device
- all methods are thread unsafe!
- *)
- LinkDevice* = POINTER TO LinkDeviceDesc;
- LinkDeviceDesc* = RECORD
- macAddr*: MacAddr; (** MAC address of the device *)
- phyWrite*: PROCEDURE(dev: LinkDevice; phyAddr, regAddr: UInt; data: UInt; VAR res: Int): BOOLEAN;
- phyRead*: PROCEDURE(dev: LinkDevice; phyAddr, regAddr: UInt; VAR data: UInt; VAR res: Int): BOOLEAN;
- setMacAddr*: PROCEDURE(dev: LinkDevice; CONST macAddr: MacAddr; VAR res: Int): BOOLEAN;
- setLinkSpeed*: PROCEDURE(dev: LinkDevice; CONST speed: ARRAY OF CHAR; fullDuplex: BOOLEAN; VAR res: Int): BOOLEAN;
- setOptions*: PROCEDURE(dev: LinkDevice; optionsPage: Int; options: SET; VAR res: Int): BOOLEAN;
- start*, stop*, reset*, finalize*: PROCEDURE(dev: LinkDevice; VAR res: Int): BOOLEAN;
- newPacket*: PROCEDURE(): Packet;
- setPacketPayload*: PROCEDURE(dev: LinkDevice; packet: Packet; CONST data: ARRAY OF CHAR; offset: Int; flags: SET; VAR res: Int): BOOLEAN;
- sendPacket*: PROCEDURE(dev: LinkDevice; packet: Packet; flags: SET; completionHandler: TaskHandler; VAR res: Int): BOOLEAN;
- updateRx*: PROCEDURE(dev: LinkDevice; VAR res: Int): BOOLEAN; (** update receive path *)
- updateTx*: PROCEDURE(dev: LinkDevice; VAR res: Int): BOOLEAN; (** update transmit path *)
- rxPacketPool*: PacketFifo;
- txPacketPool*: PacketFifo;
- linkState*: BOOLEAN; (** TRUE if the link is establshed *)
- linkSpeed*: ARRAY 32 OF CHAR; (** textual representation of the link speed in Mbps *)
- fullDuplex*: BOOLEAN; (** TRUE for full duplex link, otherwise the link is half-duplex *)
- recvPackets*: PacketFifo; (** ordered list (FIFO) of received packets *)
- isActive*: BOOLEAN; (** TRUE if device is active *)
- acquireRx*, acquireTx*: PROCEDURE{DELEGATE}();
- releaseRx*, releaseTx*: PROCEDURE{DELEGATE}();
- intf*: Interface;
- END;
- (**
- Packet FIFO buffer
- *)
- PacketFifo* = POINTER TO PacketFifoDesc;
- PacketFifoDesc* = RECORD
- packets*: POINTER TO ARRAY OF Packet;
- count*: Int; (** number of packets in the FIFO *)
- wrPos*: Int; (** write pointer position *)
- rdPos*: Int; (** read pointer position *)
- acquire*, release*: PROCEDURE{DELEGATE}(); (** locks used when thread-safety is enabled *)
- END;
- (**
- Task handler descriptor
- *)
- TaskHandler* = POINTER TO TaskHandlerDesc;
- TaskHandlerDesc* = RECORD
- res*: Int; (** result code *)
- handle*: PROCEDURE{DELEGATE}(handler: TaskHandler); (** handler procedure *)
- param*: ANY; (** handler parameter *)
- next*: TaskHandler; (** linked list of handlers *)
- (* used for task management *)
- taskExpireTime, taskInterval: EnetTiming.Time;
- prevTask, nextTask: TaskHandler;
- END;
- (**
- Initialization of a network interface descriptor
- intf: interface to initialize, must be preallocated
- *)
- PROCEDURE InitInterface*(intf: Interface);
- VAR i: Int;
- BEGIN
- intf.ipv4Addr := NilIpAddr;
- intf.ipv4Gateway := NilIpAddr;
- intf.ipv4SubnetMask := NilIpAddr;
- intf.ipv4Prefix := 0;
- intf.ipv6Addr := NilIpAddr;
- intf.ipv6Gateway := NilIpAddr;
- intf.ipv6SubnetMask := NilIpAddr;
- intf.ipv6Prefix := NilIpAddr;
- intf.numEthFrameHandlers := 0;
- FOR i := 0 TO LEN(intf.ipPacketHandlers)-1 DO intf.ipPacketHandlers[i] := NIL; END;
- intf.arpHandler := NIL;
- intf.icmpHandler := NIL;
- intf.udpHandler := NIL;
- intf.tcpHandler := NIL;
- intf.dev := NIL;
- END InitInterface;
- (**
- Return a packet to its owner pool; thread-safe in case if multithreading is enabled
- *)
- PROCEDURE ReturnPacketToOwnerPool*(packet: Packet): BOOLEAN;
- VAR b: BOOLEAN;
- BEGIN
- IF ThreadSafe THEN packet.ownerPool.acquire; END;
- b := PacketFifoPut(packet.ownerPool,packet);
- IF ThreadSafe THEN packet.ownerPool.release; END;
- RETURN b;
- END ReturnPacketToOwnerPool;
- (**
- Process received packets for agiven interface
- *)
- PROCEDURE ProcessIntfRecvPackets*(intf: Interface; VAR res: Int): BOOLEAN;
- VAR
- packet: Packet;
- packetHandler: PacketHandler;
- flags: SET;
- level4Hdr: ADDRESS;
- i: Int;
- BEGIN
- WHILE PacketFifoGet(intf.dev.recvPackets,packet) DO
- ASSERT(~packet.ownedByDev & ~packet.ownedByUser);
- packet.intf := intf;
- flags := {};
- packetHandler := NIL;
- (*
- Packet dispatching
- *)
- IF packet.ethFrameHdr.etherType = EtherTypeIpv4 THEN
- level4Hdr := ADDRESSOF(packet.ipv4Hdr^) + SIZEOF(Ipv4Hdr);
- CASE packet.ipv4Hdr.protocol OF
- |ProtoIcmp: packet.icmpHdr := level4Hdr; packetHandler := intf.icmpHandler;
- |ProtoUdp: packet.udpHdr := level4Hdr; packetHandler := intf.udpHandler;
- |ProtoTcp: packet.tcpHdr := level4Hdr; packetHandler := intf.tcpHandler;
- ELSE
- packetHandler := intf.ipPacketHandlers[Int(packet.ipv4Hdr.protocol) MOD 100H];
- END;
- ELSIF packet.ethFrameHdr.etherType = EtherTypeIpv6 THEN
- INCL(flags,FlagIpv6);
- level4Hdr := ADDRESSOF(packet.ipv6Hdr^) + SIZEOF(Ipv6Hdr);
- CASE packet.ipv6Hdr.protocol OF
- |ProtoIcmp: packet.icmpHdr := level4Hdr; packetHandler := intf.icmpHandler;
- |ProtoUdp: packet.udpHdr := level4Hdr; packetHandler := intf.udpHandler;
- |ProtoTcp: packet.tcpHdr := level4Hdr; packetHandler := intf.tcpHandler;
- ELSE
- packetHandler := intf.ipPacketHandlers[Int(packet.ipv4Hdr.protocol) MOD 100H];
- END;
- ELSIF packet.ethFrameHdr.etherType = EtherTypeArp THEN
- packetHandler := intf.arpHandler;
- ELSE
- i := 0;
- WHILE (i < intf.numEthFrameHandlers) & (intf.ethFrameHandlers[i].etherType # packet.ethFrameHdr.etherType) DO
- INC(i);
- END;
- IF i < intf.numEthFrameHandlers THEN packetHandler := intf.ethFrameHandlers[i].packetHandler; END;
- END;
- IF packetHandler # NIL THEN packetHandler(intf,packet,flags); END;
- IF ~packet.ownedByUser & ~packet.ownedByDev THEN (* put packet to the receive packet pool *)
- ASSERT(packet.ownerPool = intf.dev.rxPacketPool);
- IF ~ReturnPacketToOwnerPool(packet) THEN res := ErrRxPacketPoolFull; RETURN FALSE; END;
- END;
- END;
- res := 0;
- RETURN TRUE;
- END ProcessIntfRecvPackets;
- (**
- Process interface-specific tasks
- *)
- PROCEDURE ProcessIntfTasks*(intf: Interface; VAR res: Int): BOOLEAN;
- VAR
- t: EnetTiming.Time;
- task: TaskHandler;
- BEGIN
- (*
- execute non-periodic tasks
- *)
- IF intf.nonPeriodicTasks # NIL THEN
- IF ThreadSafe THEN intf.acquireTasks; END;
- task := intf.nonPeriodicTasks;
- t := EnetTiming.getTimeCounter();
- WHILE (task # NIL) & (task.taskExpireTime - t <= 0) DO
- (*Trace.String("executing non-periodic task...");*)
- task.handle(task);
- task := task.nextTask;
- (*Trace.StringLn(" Done!, (task=NIL)=" & (task = NIL));*)
- END;
- intf.nonPeriodicTasks := task;
- IF ThreadSafe THEN intf.releaseTasks; END;
- END;
- (*
- execute periodic tasks
- *)
- IF intf.periodicTasks # NIL THEN
- IF ThreadSafe THEN intf.acquireTasks; END;
- task := intf.periodicTasks;
- t := EnetTiming.getTimeCounter();
- WHILE (task # NIL) & (task.taskExpireTime - t <= 0) DO
- task.handle(task);
- task.taskExpireTime := t + task.taskInterval;
- task := task.nextTask;
- END;
- IF ThreadSafe THEN intf.releaseTasks; END;
- END;
- res := 0;
- RETURN TRUE;
- END ProcessIntfTasks;
- (**
- Setup an Ethernet frame handler for a given network interface
- *)
- PROCEDURE SetEthFrameHandler*(intf: Interface; etherType: Int16; packetHandler: PacketHandler);
- VAR i: Int;
- BEGIN
- ASSERT(packetHandler # NIL);
- i := 0; WHILE (i < intf.numEthFrameHandlers) & (intf.ethFrameHandlers[i].etherType # etherType) DO INC(i); END;
- ASSERT(i = intf.numEthFrameHandlers);
- IF etherType = EtherTypeArp THEN
- intf.arpHandler := packetHandler;
- ELSE
- i := intf.numEthFrameHandlers;
- intf.ethFrameHandlers[i].etherType := etherType;
- intf.ethFrameHandlers[i].packetHandler := packetHandler;
- END;
- END SetEthFrameHandler;
- (**
- Setup an IP packet handler for a given network interface
- *)
- PROCEDURE SetIpPacketHandler*(intf: Interface; protocol: Int8; packetHandler: PacketHandler);
- BEGIN
- IF protocol = ProtoIcmp THEN intf.icmpHandler := packetHandler;
- ELSIF protocol = ProtoTcp THEN intf.tcpHandler := packetHandler;
- ELSIF protocol = ProtoUdp THEN intf.udpHandler := packetHandler;
- ELSE
- intf.ipPacketHandlers[Int(protocol) MOD 100H] := packetHandler;
- END;
- END SetIpPacketHandler;
- (**
- Initialize a network packet
- packet: packet to initialize, must be preallocated
- *)
- PROCEDURE InitPacket*(packet: Packet);
- VAR
- level2Hdr: ADDRESS;
- level3Hdr: ADDRESS;
- BEGIN
- ASSERT(packet # NIL);
- ASSERT(packet.data # NIL);
- level2Hdr := ADDRESSOF(packet.data[packet.dataOffs]);
- packet.ethFrameHdr := level2Hdr;
- level3Hdr := level2Hdr + SIZEOF(EthFrameHdr);
- packet.arpHdr := level3Hdr;
- packet.ipv4Hdr := level3Hdr;
- packet.ipv6Hdr := level3Hdr;
- (* will be set up in runtime *)
- packet.icmpHdr := NIL;
- packet.udpHdr := NIL;
- packet.tcpHdr := NIL;
- END InitPacket;
- (**
- Create a packet FIFO
- allocSize: FIFO size to allocate
- *)
- PROCEDURE NewPacketFifo*(allocSize: Int): PacketFifo;
- VAR fifo: PacketFifo;
- BEGIN
- NEW(fifo);
- NEW(fifo.packets,allocSize);
- fifo.wrPos := 0;
- fifo.rdPos := 0;
- fifo.count := 0;
- RETURN fifo;
- END NewPacketFifo;
- (**
- Put an entry into a packet FIFO
- Returns TRUE in case of success, FALSE indicates that the FIFO is full
- *)
- PROCEDURE PacketFifoPut*(fifo: PacketFifo; packet: Packet): BOOLEAN;
- BEGIN
- IF fifo.count < LEN(fifo.packets) THEN
- fifo.packets[fifo.wrPos] := packet;
- INC(fifo.wrPos); IF fifo.wrPos >= LEN(fifo.packets) THEN fifo.wrPos := 0; END;
- INC(fifo.count);
- RETURN TRUE;
- ELSE
- RETURN FALSE;
- END;
- END PacketFifoPut;
- (**
- Get an entry from a packet FIFO
- Returns TRUE in case of success, FALSE indicates that the FIFO is empty
- *)
- PROCEDURE PacketFifoGet*(fifo: PacketFifo; VAR packet: Packet): BOOLEAN;
- BEGIN
- IF fifo.count > 0 THEN
- packet := fifo.packets[fifo.rdPos];
- INC(fifo.rdPos); IF fifo.rdPos >= LEN(fifo.packets) THEN fifo.rdPos := 0; END;
- DEC(fifo.count);
- RETURN TRUE;
- ELSE
- RETURN FALSE;
- END;
- END PacketFifoGet;
- (**
- Schedule a task
- task: the task handler to execute
- periodic: TRUE for a periodic task
- interval: time interval in time counts after the expiration of which the specified task will be executed
- *)
- PROCEDURE ScheduleTask*(intf: Interface; task: TaskHandler; periodic: BOOLEAN; interval: EnetTiming.Time);
- VAR
- handler, handlerPrev: TaskHandler;
- PROCEDURE PutTask(VAR taskList: TaskHandler; task: TaskHandler);
- BEGIN
- (* put the task into the list of tasks - the earliest task to execute first *)
- IF taskList # NIL THEN
- handler := taskList;
- IF task.taskExpireTime - handler.taskExpireTime <= 0 THEN (* the task is the earliest to execute *)
- task.prevTask := NIL;
- task.nextTask := handler;
- handler.prevTask := task;
- taskList := task;
- ELSE
- REPEAT
- handlerPrev := handler;
- handler := handler.nextTask;
- UNTIL (handler = NIL) OR (task.taskExpireTime - handler.taskExpireTime <= 0);
- IF handler # NIL THEN (* put the task in between handlerPrev and handler *)
- task.prevTask := handler.prevTask;
- task.nextTask := handler;
- handler.prevTask := task;
- ELSE (* the task is the latest to execute *)
- task.prevTask := handlerPrev;
- task.nextTask := NIL;
- handlerPrev.nextTask := task;
- END;
- END;
- ELSE
- task.prevTask := NIL;
- task.nextTask := NIL;
- taskList := task;
- END;
- END PutTask;
- BEGIN
- ASSERT(task.handle # NIL);
- ASSERT(interval > 0);
- IF ThreadSafe THEN intf.acquireTasks; END;
- task.taskExpireTime := EnetTiming.getTimeCounter() + interval;
- IF periodic THEN
- task.taskInterval := interval;
- PutTask(intf.periodicTasks,task);
- ELSE
- PutTask(intf.nonPeriodicTasks,task);
- END;
- IF ThreadSafe THEN intf.releaseTasks; END;
- END ScheduleTask;
- (**
- Remove a scheduled task
- *)
- PROCEDURE RemoveTask*(intf: Interface; task: TaskHandler);
- BEGIN
- IF ThreadSafe THEN intf.acquireTasks; END;
- IF task.prevTask # NIL THEN
- task.prevTask.nextTask := task.nextTask;
- ELSIF task = intf.nonPeriodicTasks THEN
- intf.nonPeriodicTasks := task.nextTask;
- ELSIF task = intf.periodicTasks THEN
- intf.periodicTasks := task.nextTask;
- END;
- IF task.nextTask # NIL THEN task.nextTask.prevTask := task.prevTask; END;
- task.nextTask := NIL;
- task.prevTask := NIL;
- IF ThreadSafe THEN intf.releaseTasks; END;
- END RemoveTask;
- PROCEDURE LinkTaskHandlers*(chain, handlerToLink: TaskHandler);
- BEGIN
- ASSERT(chain # handlerToLink);
- handlerToLink.next := chain.next;
- chain.next := handlerToLink;
- END LinkTaskHandlers;
- (* Returns TRUE if a given character represents a decimal digit *)
- PROCEDURE IsDecDigit(ch: CHAR): BOOLEAN;
- BEGIN
- RETURN (ORD(ch) >= ORD('0')) & (ORD(ch) <= ORD('9'));
- END IsDecDigit;
- (* Returns TRUE if a given character represents a hexadecimal digit *)
- PROCEDURE IsHexDigit(ch: CHAR): BOOLEAN;
- BEGIN
- RETURN ((ORD(ch) >= ORD('0')) & (ORD(ch) <= ORD('9'))) OR ((ORD(CAP(ch)) >= ORD('A')) & (ORD(CAP(ch)) <= ORD('F')));
- END IsHexDigit;
- PROCEDURE DecDigitToInt(digit: CHAR): Int;
- BEGIN
- RETURN ORD(digit) - ORD('0');
- END DecDigitToInt;
- PROCEDURE HexDigitToInt(digit: CHAR): Int;
- BEGIN
- IF IsDecDigit(digit) THEN
- RETURN ORD(digit) - ORD('0');
- ELSE
- RETURN ORD(CAP(digit)) - ORD('A') + 10;
- END;
- END HexDigitToInt;
- (* Parse a word from a string representation of an IP or MAC address *)
- PROCEDURE ParseAddrWord(
- CONST strIp: ARRAY OF CHAR;
- VAR pos: Int;
- delim: CHAR;
- maxNumDigits: Int;
- hex, lastWord: BOOLEAN;
- VAR word: Int
- ): BOOLEAN;
- VAR
- k, len: Int;
- strWord: ARRAY 8 OF CHAR;
- digitCondProc: PROCEDURE(ch: CHAR): BOOLEAN;
- BEGIN
- IF ~hex THEN digitCondProc := IsDecDigit;
- ELSE digitCondProc := IsHexDigit;
- END;
- len := 0;
- WHILE (pos < LEN(strIp)) & digitCondProc(strIp[pos]) & (len < maxNumDigits) DO strWord[len] := strIp[pos]; INC(pos); INC(len); END;
- IF ~lastWord THEN
- IF (pos = LEN(strIp)) OR (strIp[pos] # delim) OR (len = 0) THEN RETURN FALSE; END;
- ELSE
- IF (pos = LEN(strIp)) OR (strIp[pos] # 0X) OR (len = 0) THEN RETURN FALSE; END;
- END;
- strWord[len] := 0X;
- IF ~hex THEN (* decimal *)
- word := DecDigitToInt(strWord[0]);
- k := 1; DEC(len);
- WHILE len > 0 DO
- word := word*10 + DecDigitToInt(strWord[k]);
- INC(k); DEC(len);
- END;
- ELSE (* hexadecimal *)
- word := HexDigitToInt(strWord[0]);
- k := 1; DEC(len);
- WHILE len > 0 DO
- word := word*16 + HexDigitToInt(strWord[k]);
- INC(k); DEC(len);
- END;
- END;
- RETURN TRUE;
- END ParseAddrWord;
- (**
- Compose an IP address given its textual representation
- *)
- PROCEDURE StrToIpAddr*(CONST strIpAddr: ARRAY OF CHAR; VAR ipAddr: IpAddr): BOOLEAN;
- VAR
- d: ARRAY 8 OF Int;
- i, k: Int;
- BEGIN
- i := 0;
- IF ParseAddrWord(strIpAddr,i,".",3,FALSE,FALSE,d[0]) THEN
- FOR k := 1 TO 3 DO
- INC(i);
- IF ~ParseAddrWord(strIpAddr,i,".",3,FALSE,k=3,d[k]) THEN RETURN FALSE; END;
- END;
- ipAddr.addr[0] := Int32(d[3])*1000000H + Int32(d[2])*10000H + Int32(d[1])*100H + Int32(d[0]);
- ipAddr.ver := 4;
- RETURN TRUE;
- ELSIF ParseAddrWord(strIpAddr,i,":",4,TRUE,FALSE,d[0]) THEN
- FOR k := 1 TO 7 DO
- INC(i);
- IF ~ParseAddrWord(strIpAddr,i,".",3,FALSE,k=7,d[k]) THEN RETURN FALSE; END;
- END;
- (*!TODO: not sure whether this is correct, check it *)
- ipAddr.addr[0] := Int32(d[0])*10000H + Int32(d[1]);
- ipAddr.addr[1] := Int32(d[2])*10000H + Int32(d[3]);
- ipAddr.addr[2] := Int32(d[4])*10000H + Int32(d[5]);
- ipAddr.addr[3] := Int32(d[6])*10000H + Int32(d[7]);
- ipAddr.ver := 6;
- RETURN TRUE;
- END;
- RETURN FALSE;
- END StrToIpAddr;
- (**
- Write a textual representation of an IP address.
- *)
- PROCEDURE IpAddrToStr*(CONST ipAddr: IpAddr; VAR strIpAddr: ARRAY OF CHAR): BOOLEAN;
- VAR
- int: ARRAY 16 OF CHAR;
- BEGIN
- IF ~IsValidIpAddr(ipAddr) THEN RETURN FALSE END;
- IF ipAddr.ver = 4 THEN
- EnetUtils.IntToStr(ipAddr.addr[0] MOD 100H, strIpAddr);
- EnetUtils.StrAppend(strIpAddr, ".");
- EnetUtils.IntToStr(ipAddr.addr[0] DIV 100H MOD 100H, int);
- EnetUtils.StrAppend(strIpAddr, int);
- EnetUtils.StrAppend(strIpAddr, ".");
- EnetUtils.IntToStr(ipAddr.addr[0] DIV 10000H MOD 100H, int);
- EnetUtils.StrAppend(strIpAddr, int);
- EnetUtils.StrAppend(strIpAddr, ".");
- EnetUtils.IntToStr(ipAddr.addr[0] DIV 1000000H MOD 100H, int);
- EnetUtils.StrAppend(strIpAddr, int);
- ELSIF ipAddr.ver = 6 THEN
- ELSE
- RETURN FALSE
- END;
- RETURN TRUE
- END IpAddrToStr;
- (**
- Compose a MAC address given its textual representation
- *)
- PROCEDURE StrToMacAddr*(CONST strMacAddr: ARRAY OF CHAR; VAR macAddr: MacAddr): BOOLEAN;
- VAR
- i, k, d: Int;
- BEGIN
- i := 0;
- IF ParseAddrWord(strMacAddr,i,":",2,TRUE,FALSE,d) THEN
- macAddr.addr[0] := Int8(d);
- FOR k := 1 TO 5 DO
- INC(i);
- IF ~ParseAddrWord(strMacAddr,i,":",3,TRUE,k=5,d) THEN RETURN FALSE; END;
- macAddr.addr[k] := Int8(d);
- END;
- RETURN TRUE;
- END;
- RETURN FALSE;
- END StrToMacAddr;
- (** Returns TRUE if a given IP address is valid *)
- PROCEDURE IsValidIpAddr*(CONST ipAddr: IpAddr): BOOLEAN;
- BEGIN
- RETURN (ipAddr.ver = 4) OR (ipAddr.ver = 6);
- END IsValidIpAddr;
- (**
- Setup an Ethernet frame
- *)
- PROCEDURE SetEthFrame*(
- packet: Packet;
- CONST srcMacAddr, dstMacAddr: MacAddr;
- etherType: Int16;
- dataLen: Int
- );
- BEGIN
- packet.ethFrameHdr.dstMacAddr := dstMacAddr;
- packet.ethFrameHdr.srcMacAddr := srcMacAddr;
- packet.ethFrameHdr.etherType := etherType;
- packet.payloadOffs := SIZEOF(EthFrameHdr);
- packet.dataLen := dataLen+SIZEOF(EthFrameHdr);
- END SetEthFrame;
- (**
- Setup an IP packet header
- *)
- PROCEDURE SetIpPacket*(
- packet: Packet;
- CONST srcMacAddr, dstMacAddr: MacAddr;
- CONST srcIpAddr, dstIpAddr: IpAddr;
- protocol: Int8;
- ipDataLen: Int
- );
- BEGIN
- ASSERT(srcIpAddr.ver = dstIpAddr.ver);
- IF srcIpAddr.ver = 4 THEN
- SetEthFrame(packet,srcMacAddr,dstMacAddr,EtherTypeIpv4,ipDataLen+SIZEOF(Ipv4Hdr));
- packet.ipv4Hdr.verAndIhl := 0x40 + (SIZEOF(Ipv4Hdr) DIV 4);
- packet.ipv4Hdr.dscpAndEcn := Int8(IpDiffServ+IpEcn); (* type-of-service on outgoing datagrams *)
- IF LittleEndianSystem THEN
- packet.ipv4Hdr.length := EnetUtils.SwitchEndianness16(Int16(ipDataLen+SIZEOF(Ipv4Hdr)));
- ELSE
- packet.ipv4Hdr.length := Int16(ipDataLen+SIZEOF(Ipv4Hdr));
- END;
- packet.ipv4Hdr.fragmentId := 0; (*! since no fragmentation is allowed the identification field is omitted (according to RFC6864) *)
- packet.ipv4Hdr.flagsAndFragmentOffs := 0x0040; (* do not allow fragmentation *)
- packet.ipv4Hdr.ttl := Int8(IpTtl);
- packet.ipv4Hdr.protocol := protocol;
- packet.ipv4Hdr.checksum := Int16(0xd0f5); (*!TODO: compute checksum if the link device does not support IP checksum offload *)
- packet.ipv4Hdr.srcIpAddr := srcIpAddr.addr[0];
- packet.ipv4Hdr.dstIpAddr := dstIpAddr.addr[0];
- INC(packet.payloadOffs,SIZEOF(Ipv4Hdr));
- ELSE
- HALT(100);
- END;
- END SetIpPacket;
- (**
- Setup a UDP packet
- *)
- PROCEDURE SetUdpPacket*(
- packet: Packet;
- CONST srcMacAddr, dstMacAddr: MacAddr;
- CONST srcIpAddr, dstIpAddr: IpAddr;
- srcPort, dstPort: Int;
- udpDataLen: Int
- );
- BEGIN
- SetIpPacket(packet,srcMacAddr,dstMacAddr,srcIpAddr,dstIpAddr,ProtoUdp,udpDataLen+SIZEOF(UdpHdr));
- IF srcIpAddr.ver = 4 THEN
- packet.udpHdr := ADDRESSOF(packet.ipv4Hdr.verAndIhl) + SIZEOF(Ipv4Hdr);
- IF LittleEndianSystem THEN
- packet.udpHdr.length := EnetUtils.SwitchEndianness16(Int16(udpDataLen+SIZEOF(UdpHdr)));
- ELSE
- packet.udpHdr.length := Int16(udpDataLen+SIZEOF(UdpHdr));
- END;
- ELSE
- HALT(100);
- END;
- IF LittleEndianSystem THEN
- packet.udpHdr.srcPort := EnetUtils.SwitchEndianness16(Int16(srcPort));
- packet.udpHdr.dstPort := EnetUtils.SwitchEndianness16(Int16(dstPort));
- ELSE
- packet.udpHdr.srcPort := Int16(srcPort);
- packet.udpHdr.dstPort := Int16(dstPort);
- END;
- packet.udpHdr.checksum := 0;
- INC(packet.payloadOffs,SIZEOF(UdpHdr));
- END SetUdpPacket;
- (**
- Returns TRUE if an IP address belongs to the same subnetwork as the IP address of a given interface
- *)
- PROCEDURE IpAddrFromSameSubnet*(intf: Interface; CONST ipAddr: IpAddr): BOOLEAN;
- BEGIN
- IF ipAddr.ver = 4 THEN
- RETURN (S.VAL(Int32,S.VAL(SET,UInt(ipAddr.addr[0])) * S.VAL(SET,UInt(intf.ipv4SubnetMask.addr[0]))) = intf.ipv4Prefix)
- OR (ipAddr = BroadcastIpAddr);
- ELSIF ipAddr.ver = 6 THEN
- RETURN (S.VAL(Int32,S.VAL(SET,UInt(ipAddr.addr[0])) * S.VAL(SET,UInt(intf.ipv6SubnetMask.addr[0]))) = intf.ipv6Prefix.addr[0]) &
- (S.VAL(Int32,S.VAL(SET,UInt(ipAddr.addr[1])) * S.VAL(SET,UInt(intf.ipv6SubnetMask.addr[1]))) = intf.ipv6Prefix.addr[1]) &
- (S.VAL(Int32,S.VAL(SET,UInt(ipAddr.addr[2])) * S.VAL(SET,UInt(intf.ipv6SubnetMask.addr[2]))) = intf.ipv6Prefix.addr[2]) &
- (S.VAL(Int32,S.VAL(SET,UInt(ipAddr.addr[3])) * S.VAL(SET,UInt(intf.ipv6SubnetMask.addr[3]))) = intf.ipv6Prefix.addr[3])
- ELSE
- RETURN FALSE;
- END;
- END IpAddrFromSameSubnet;
- (**
- Returns TRUE if the two IP addresses are equal
- *)
- OPERATOR "="*(CONST ipAddr0, ipAddr1: IpAddr): BOOLEAN;
- BEGIN
- IF ipAddr0.ver = ipAddr1.ver THEN
- IF ipAddr0.ver = 4 THEN RETURN ipAddr0.addr[0] = ipAddr1.addr[0];
- ELSE RETURN (ipAddr0.addr[0] = ipAddr1.addr[0]) & (ipAddr0.addr[1] = ipAddr1.addr[1]) & (ipAddr0.addr[2] = ipAddr1.addr[2]) & (ipAddr0.addr[3] = ipAddr1.addr[3]);
- END;
- ELSE RETURN FALSE;
- END;
- END "=";
- (**
- Returns TRUE if the two IP addresses are not equal
- *)
- OPERATOR "#"*(CONST ipAddr0, ipAddr1: IpAddr): BOOLEAN;
- BEGIN
- RETURN ~(ipAddr0 = ipAddr1);
- END "#";
- VAR
- NilMacAddr-: MacAddr; (** NIL MAC address *)
- BroadcastMacAddr-: MacAddr; (** broadcast MAC address *)
- NilIpAddr-: IpAddr; (** NIL IP address *)
- BroadcastIpAddr -: IpAddr; (** broadcast IPv4 address *)
- PROCEDURE InitMod;
- VAR i: Int;
- BEGIN
- Trace.string := EnetEnvironment.TraceString;
- EnetTiming.getTimeCounter := EnetEnvironment.GetTimeCounter;
- EnetTiming.fromMicro := EnetEnvironment.FromMicro;
- EnetTiming.fromMilli := EnetEnvironment.FromMilli;
-
- FOR i := 0 TO LEN(NilMacAddr.addr)-1 DO
- NilMacAddr.addr[i] := 0x00;
- BroadcastMacAddr.addr[i] := Int8(0xFF);
- END;
- NilIpAddr.addr[0] := 0x00;
- NilIpAddr.addr[1] := 0x00;
- NilIpAddr.addr[2] := 0x00;
- NilIpAddr.addr[3] := 0x00;
- NilIpAddr.ver := 0;
- ASSERT(StrToIpAddr("255.255.255.255", BroadcastIpAddr));
- END InitMod;
- BEGIN
- InitMod;
- END EnetBase.
|