Skip to content

Custom GERTi Implementations

Major General Relativity edited this page May 11, 2020 · 9 revisions

I. Introduction

GERTi is a networking protocol for OpenComputers that provides networking services across wired cards, wireless cards, and linked cards. While the complete GERT protocol allows for communication across the Internet, GERTi is only for communication within a Minecraft Server/World. This document explains the working of the backend and API offered on OpenOS, to help Operating System developers provide their own version of GERTi. A certain level of backend implementation is mandatory, but offering an identical API to the official OpenOS version is only recommended. This means that support for connections, paths, and message handling is required, but offering sockets is not required.

II. Compatibility.

All implementations of GERTi will be interoperable and fully compatible, due to implementation of the same backend. GERTi can be made fully compatible with GERTe by installing the GERTeAPI on the GERTiMNC.

III. Technical Terminology

Terms found in the Terms section at https://github.com/GlobalEmpire/GERT/wiki/GERTi will not be re-listed here, with the exception of API.

API: The GERTi API is the forward facing API that programmers developing programs for GERTi will call. An in-depth feature discussion is in Section 5, but it provides functionality such as sockets, determining a computer's address, and more. Although not recommended, this may vary across OS implementations of GERTi

Backend: The GERTi backend is the collection of functions and event handlers that are responsible for handling everything from data transmission, to network node management. The backend is not designed to be accessible to programmers utilizing GERTi, and must support the same functionality across GERTi implementations.

Connection: A GERT connection exists at the two endpoints of the connection, and on any node in between the two. A connection is defined and indexed by its connectionIndex, which is created by compositing the origination, destination, and ID as Origination.."|"..Destination.."|"..ID. Connections store the origination, destination, ID. If this is an endpoint, it can store the data and order fields. If it is a midpoint, it must store the nextHop and port fields.

Modem: From this point on, a modem is assumed to include wired cards, wireless cards, and linked cards unless specified otherwise.

Neighbor: A neighbor is a node that is directly reachable from a specified node. That is, a regular modem.send will work for transmitting a message.

Official Implementation: The official implementation is the OpenOS implementation of GERT found in the Global Empire GERT GitHub repository.

Tier: The tier of a node is a measure of how many hops it will take to get to a MNC, which is Tier 0. Tier numbers higher than 900 are not supported, which means any node is at most 1800 hops away from each other.

IV. GERTi Client Backend

A. Stored data

  1. The Neighbor Table A node must keep a record of the next node on the path to the MNC. A table of neighbors is recommended to improve performance and reduce potential network bottlenecking. The official implementation utilizes a table indexed by a neighbor's GERTi address, with fields for the modem address, port, and tier. The port field is used as a means of quickly keeping track of whether the neighbor is using either a linked card, or a wired/wireless card.

  2. The Connection Table A node must support at least one open connection. If only one connection at a time is supported, a simple data structure can be used, and the node can be configured to refuse any further connection attempts. If the implementation supports more than one connection, a table structure may be used to store connections. The official implementation uses a set of subtables indexed by connectionIndex. The data and order variables are stored as further subtables in a connection.

  3. The Data Table It is not required for an implementation to internally store data, but it is strongly recommended, as this will allow for buffering data, re- ordering, and good socket support. The official implementation stores the data table inside of a connection table entry, but this is not mandatory.

B. Connectivity Support

Any GERTi implementation must support connecting over wired, wireless, and linked cards. This will allow for a user to install GERTi without worrying about what network interface they need to use. The official implementation provides this support by checking for and then registering a wired/wireless and/or linked card. If a network card is detected, it checks for wireless connectivity and automatically tries to set an appropriate signal strength. The "transInfo" function checks the port of an outgoing data request, and then determines the appropriate interface. Linked card traffic can always be distinguished due to the port always being 0.

C. GERTi Message Flags

  1. Definition

    GERTi Message Flags are modem messages sent by GERTi nodes at various points during their lifetimes that are used to trigger responses in other nodes. GERTi Message Flags can be checked for by reading the first "data" parameter of any modem_message event, that is the first parameter after the packet identification parameters. An implementation must support the full suite of message flags.

  2. CloseConnection

    This flag is used to indicate when a connection should be closed. The proper behavior is to close the connection if it ends in this node, or to forward on the message to the next hop in the path

  3. Data

    This flag is used to indicate an incoming data packet. This flag is further discussed in Section F, "Data Handling."

  4. NewNode

    This flag is used to indicate a new node joining the network. If a client receives the flag with no parameters after the flag code, it should respond with its tier and GERTi address. If a client receives the flag with an address and tier parameter after the flag code, it should instead store that information as a new neighbor.

  5. OpenRoute

    This flag is fired whenever a connection is being opened from another GERTi node. This flag is further discussed in Section E, "Connection Opening."

  6. RemoveNeighbor

    This flag is fired whenever another node is attempting to disconnect from the network. This should be handled by deleting the node from the neighbor table if it is a neighbor. Whether it is a neighbor or not, the RemoveNeighbor flag should be transmitted to the highest tier neighbor this node posseses, so that the MNC can update its master records.

  7. RegisterNode

    This flag is used to indicate that another node is attempting to acquire a GERTi address. This flag should be retransmitted to the highest tier neighbor this node posseses, and it should await a "RegisterComplete" flag. Once the "RegisterComplete" flag is received, it should be retransmitted back to where the "RegisterNode" flag came from. This will allow a new node to receive its GERTi address.

  8. RegisterComplete

 See section 7
  1. Receiving Packets

    This is not a flag. However, a simple packet reception function can be set up to receive a modem_message event or equivalent, and then check for a GERTi Message Flag. A table of flag handlers can be stored and then called based on the flag set in the message. While having this exact setup is not necessary, it is how it is done in the official implementation.

D. Startup and Shutdown

  1. Definition

    GERTi uses a 3 phase startup procedure and 2 phase shutdown shutdown procedure. This allows for graceful adjustment of the network. The startup and shutdown procedures must be followed in their entirety, as it is important in allowing for proper network flexibility.

  2. Startup

    The first phase is to announce the presence of the node on the network. This is done by transmitting a data packet on port 4378 with "NewNode" as the only parameter. This should be done on all network interfaces available, and using broadcast for the modem component. After transmitting the message, an event handler should be registered to listen for incoming messages with the first parameter being "RETURNSTART". RETURNSTART messages should be handled appropriately as in section C9.

    The second phase occurs after a variable amount of time, but the recommended wait time is 2 seconds. At this point, a node should serialize its neighbor table and transmit it to the highest tier (closest to 0) neighbor it posseses, with a "RegisterNode" message. The "RegisterNode" message should have the second parameter be the desired, usually the primary, modem or tunnel address of the node, the third be the node's tier, and the fourth be the serialized neighbor table. At this point, the node should await a "RegisterComplete" message. Once received, the "RegisterComplete" message's second parameter should be check to see if it matches the modem/tunnel address of the node, and if it does, the third parameter should be stored as the GERTi address.

    The third phase is to send a "RETURNSTART" to all neighbors and announce the node's GERTi address so the neighbors can store the new node in their neighbor tables. After this, the node may optionally prepare a shutdown procedure. If used in an OS environment, this is strongly recommended, while it can be skipped for EEPROMs.

  3. Shutdown

    The node closes all open connections, then broadcasts a "RemoveNeighbor" message with the node's GERTi address being the second parameter. Closing connections is done by transmitting a "CloseConnection" message with the ID, destination, and origination of the connection. After all connections are closed and the node has disconnected from the network, GERTi shutdown is complete.

E. Connection Opening

  1. Definition

    GERTi opens connections in two phases. The first phase involves the origination computer sending a connection open message, which is then relayed to the destination. The second phase consists of the destination computer registering the connection on its end and sending a message back to the origination. Socket implmentation is NOT discussed in this section. This section only covers the process defined by routeOpener and handler.OpenRoute in the official implementation.

  2. First Phase

    The first phase starts when the origination node begins setting up a route after being provided with the origination, destination, and connectionID. The official implementation uses the same function used when a node becomes an intermediate, but other implementations can use separate functions. To reduce network traffic, the origination node should check if the destination is a neighbor. If it is, then a direct connection can be established. Otherwise, the node should send the connection request should be sent to the highest tier (closest to 0) node in the neighbor table. The connection request should have the first parameter be "OpenRoute", the second parameter be the destination GERTi address, the third be nil, the fourth be the origination GERTi address, and the fifth be the connection ID.

    Intermediate nodes should handle the "OpenRoute" parameter by checking to see if the destination is a neighbor or if the intermediate parameter is specified. If either is true, the request should be forwarded to the appropriate node. If the intermediate parameter is not nil, it is formatted in the manner of hop1|hop2|hop3|... A node should break out hop1 and delete it and the first pipe symbol from the intermediate parameter. Hop1 can be looked up using the neighbor table and the OpenRoute request can be passed on.

    If neither condition is true, the intermediate should forward the request to the highest tier (closest to 0) node in the neighbor table.

    Destination nodes are determined by every node in the path checking to see if the destination variable matches that node's GERTi address. If it is the destination, then it should not forward the connection request and should instead process it. The connection is processed by adding it to the connection table (see A2). Once that is done, it means the outgoing connection is initialized and the first phase is complete.

  3. Second Phase

    The second phase starts when the destination node sends the "RouteOpen" message. The first parameter of the message should be "RouteOpen", the second should be the destination GERTi address, and the third should be the origination GERTi address.

    Intermediate nodes should be ready to handle "RouteOpen" messages and verify the destination+origination before forwarding them on. The official implementation uses a temporary event listener that receives the "RouteOpen" message, verifies the destination+origination and then forwards it to the stored next hop. It should also generate the connectionIndex and add an entry to the connection table with the nextHop and port for future routing.

    The original computer should receive the "RouteOpen" message, verify the destination, and then configure the connections table as desired. Once this is complete, the bidirectional connection is set.

  4. Failure

    In the event that a destination can not be found, a "RouteOpen" message should not be returned at any point in time. The client may choose to implement handling this error condition in any way desired. The official implementation simply does not create any connection entries and thus allows the program opening a connection to determine the failure.

F. Data Handling

  1. If the Destination is not the Node

    If the data is not intended for the node, look up the next hop in the connections table using the provided connectionIndex. Forward the data packet on to the next node.

  2. If the Destination is the Node

    Store the data in whatever fashion is so desirable. Buffering data in connections is optional, but recommended. Utilizing the embedded order variable is optional. Utilizing it allows for re-ordering of packets and transmission verification.

V. GERTi API

Not all API functions need to be handled, but these are suggestions for recommended functions to implement.

  1. getAddress

    Return the GERTi address of the node.

  2. getConnections

    Return the connection table of the node. The default implementation strips out all stored data, but this is an optional step.

  3. getNeighbors

    Return the neighbor table of the node.

  4. getVersion

    Returns 2 strings. One is a string containing a human readable version number like "GERTi v1.2 Hotfix 1". The second is a semantic versioned string like "v1.2.2".

  5. send

    Directly transmit data without opening a connection. The connectionID must be set to -1 to allow the destination to determine that the send function was used. Routing sends past one logical hop is not implemented in the official implementation, but is allowed for other implementations.

  6. broadcast

    Identical to send, except it uses modem.broadcast() instead.

  7. openSocket and the socket object

    openSocket is the primary data transmission function in GERTi. openSocket takes two parameters, destination and connectionID, and then uses them to open a connection. The official implementation contains a slot for a third parameter, but this is unused and deprecated. openSocket, after being called, attempts to open a connection. The official implmentation handles some of the connection logic in the openSocket function itself, but this can vary based on the implementation. Calling openSocket must always result in either new entries in the connection and path tables and a returned table variable if it succeeds, or an error/nil value if it fails.

    The socket object must contain the following fields: Destination: destination GERTi address ID: connectionID write: A link to a function that writes data to the connection. Calling socket:write(data) should write data to the connection. Support for types that cannot be caried over modem messages directly is optional. close: A link to a function that closes the socket and the connections tied to it.

    The socket object may contatin the following fields: read: A link to a function that reads data from the connection. Calling local data = socket:read() should return all data buffered in the socket. If data is not buffered and a blocking read design is not desired, this may be omitted.

VI. GERTiMNC Functionality

A. Differences from GERTi Client

  1. The GERTiMNC utilizes more complex data structures than a GERTiClient to allow for storing every node in the network, and keeping track of their heirarchical relationships.
  2. It includes file I/O capability to allow for storing GERTi addresses.
  3. It has no API.
  4. It integrates optional GERTe functionality.

B. Specialized MNC Functionality

  1. Node Table

    The GERTiMNC must maintain a map of every node on the network, and their neighbors. The official implementation does this via a table structured node[address]["tierofNeighbor"][neighborAddress] to keep track of neighbor nodes that are higher and lower tier. Custom implementations can use whatever method desired, but a complete network map must be stored during run time. Storing the network map past a shutdown is optional and may be undesirable.

  2. Registering Nodes

    The GERTiMNC is responsible for registering nodes. During node registration, the network map should be expanded to include the new node, linking it to existing nodes, and linking existing nodes to it. Then, the new node should have an address assigned. The address must be transmitted back to the client in a message with the first parameter being "RegisterComplete", the second being the modem address of the new node, and the third being the GERTi address of the new node.

  3. Routing Connections

    If an OpenRoute message reaches the MNC, it must use the network map to find a connection if possible. If the route involves more than two hops past the MNC, it must make use of the intermediary parameter supported in the OpenRoute message. If the connection involves a GERTe address, it should either be dropped if GERTe is not integrated, proceed according to instructions in section B4, or (optional) attempt to determine if the GERTe address is different. If the GERTe address is the same address as the MNC, the GERTe address may be stripped and the message can be routed internally. However, the origination is expecting packets with the GERTe address, so this must be stored and handled appropriately.

  4. GERTe Integration

    The MNC is the point responsible for handling conversion between the GERTe and GERTi protocols. A MNC may require GERTe to function, but this is not recommended. If GERTe integration is desired, the MNC should be capable of storing its GERTe address and key. The combination of the two are used for connecting to a GEDS and joining the GERTe network. After startup, a MNC should call GERTe.startup(), which will initiate a connection. The client should then call GERTe.register(GERTeAddress, GERTeKey) to fully join the GERTe network. After this point, the MNC can call GERTe.parse to check for incoming messages. If GERTe.parse returns an incoming message, it should be processed.

    GERTe messages are formated as tables with 3 indices, target, source, and data. Target is the GERTc (GERTe+i) address of the destination computer, e.g 1879.1:0.5. Source is the same, but for the origination computer. Data is the data contained in the message.

    GERTe.parse only returns one message at a time, so it should be continually checked to make sure the queue is empty. On shutdown, the MNC should call GERTe.shutdown() to ensure the connection is closed gracefully.

    Custom GERTeAPIs are supported, but outside the scope of this document. This only describes interaction with the official OC GERTe API.

Revision 1.3 created on May 11, 2020