Creating A New Protocol

Creating a new protocol in plexus is simple. The first step is to define the protocol's header in an interface file. Plexus follows the naming convention of protocolPktFormat.i3 for each protocol. This interface file describes the header layout of the protocol as a Modula-3 RECORD and any other constants used by the protocol. The RECORD must be packed to prevent the compiler from padding it. Following the Modula-3 naming convetion, the protocolPktFormat.T type defines the protocol's header type. For example, the interface for the Ethernet protocol looks like:

INTERFACE EtherPktFormat;
TYPE EtherType = BITS 16 FOR [16_0 .. 16_ffff];
TYPE T = RECORD
  dhost   : ARRAY [1..6] OF CHAR;
  shost   : ARRAY [1..6] OF CHAR;
  type    : EtherType;
END;

CONST
  ETHERTYPE_IP  : EtherType= 16_0800; (* IP protocol number        *)
  ETHERTYPE_ARP : EtherType= 16_0806; (* Addr. resolution protocol *)

END EtherPktFormat;
The next step is to define the protocol events. All Plexus protocols define a PacketArrived event. This event roughly corresponds to the input function found in BSD style protocol stacks, and serves as the binding points for extending a protocol with customized a implementation. The Modula-3 type signature of the PacketArrived event is:
TYPE PacketArrivedEvent = PROCEDURE (packet, curr: Mbuf.T; offset: CARDINAL):BOOLEAN;
By convention this event is defined in a protocol.i3 interface file. Packet data is passed up and down protocol layers using the mbuf datastructure. Plexus mbufs are similar to the Berkeley memory buffers found in conventional Unix operating systems. This datastructure is used more as a matter of convince, as it works directly with the device drivers that are using in SPIN. The packet parameter is an mbuf pointer to the beginning of a packet chain. The curr parameter is a pointer to the mbuf that contains the packet payload. It is very likely that for low-layered protocols both packet and curr point at the same mbuf. However, for high-layer protocols, such as Udp and Tcp, the header and data may not be contained in the initial mbuf. The curr mbuf pointer prevents us from having to walk the linked list of the mbuf chain to find the payload that is currently being processed.

To make life easier, there is a quake directive, called protocolModule, that instantiates a generic interface and module. The generic defines the PacketArrived event and (un)install procedures used by clients that attach themselves to the event. The directive comes in the conventional m3build forms, where the lower case version hides the module and the upper case version allows other modules to link/import with the interface. Here's an example of the Ether protocol begin instantiated with the m3build directive:

 
ProtocolModule ("Ether","EtherServer") # visible
protocolmodule ("Ether","EtherServer") # hidden
Note the different cases of the visible and hidden protocolmodule directives. The first argument to the directive is the name of the protocol. The second argument is the domain name that the protocol is defined in. The protocolmodule is just a bit of glue for the following quake commands:
proc ProtocolModule (protocol,link) is
  local inf   = link & "Interface"
  build_generic_intf (protocol, "Protocol", [], VISIBLE)
  build_generic_impl (protocol, "Protocol", [inf])
end
Marc Fiuczynski
DAY/MONTH/YEAR