Skip to content

Commit

Permalink
Add DCCP example
Browse files Browse the repository at this point in the history
Ref. eng/recordflux/RecordFlux#1362
  • Loading branch information
treiher committed Oct 13, 2023
1 parent 288850e commit aa6d442
Show file tree
Hide file tree
Showing 56 changed files with 12,323 additions and 0 deletions.
3 changes: 3 additions & 0 deletions examples/apps/dccp/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ada.projectFile": "build.gpr"
}
11 changes: 11 additions & 0 deletions examples/apps/dccp/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.PHONY: test build prove generate clean

test:

build:

prove:

generate:

clean:
68 changes: 68 additions & 0 deletions examples/apps/dccp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# DCCP

## Getting started

This project provides a simplified example set for a DCCP specification with a "client" and "server" application. Additional details about what each application does are described below.

## DCCP Server

This application acts as the "server," listening on a specific local port for messages in the DCCP format. It is able to receive a specific set of messages based on a small Wireshark sample data set. These messages include:

- DCCP Request
- DCCP Response
- DCCP Ack
- DCCP DataAck
- DCCP Data
- DCCP Close
- DCCP Reset

If one of the above messages is received in the expected format, the message information will be printed to the console window.

This application constantly "listens" for a message and will provide no further indication of operation until a message is received.

## DCCP Client

This application acts as the "client," sending a specific set of DCCP messages to a local port. This specific set of messages was pulled from a Wireshark DCCP sample set ([Link to archive](https://wiki.wireshark.org/uploads/__moin_import__/attachments/SampleCaptures/dccp_trace.pcap.gz)).

For simplicity, and to demonstrate how to write to/access message fields, these messages are hard-coded within the DCCP Client application.

## Using the Applications

The basic setup for these applications is simple. After compiling the code into an executable format:

1. Run the DCCP Server
2. Run the DCCP Client

Once the client is running and prints out message status information, the server should start receiving the same respective information and print the results to the console.

## DCCP Specification

RecordFlux was used to generate the "DCCP specification" for this project. Note that the DCCP protocol was **NOT** implemented in its entirety. The core of the protocol is the **Generic Header**; a fairly simple implementation of this is implemented in RecordFlux. A small portion of **Additional Fields** and **Options** are also implemented so as to align with the Wireshark sample data set. Additionally, the **Application Data Area** of the protocol is lightly implemented here based on Wireshark data.

The DCCP Specification file is located in the **\specs** folder of each application.

The message graph (generated from the specification file) is located in the **\out** folder of each application. This provides a visual graph of the message itself, which can be useful for understanding and/or message interpretation.

## HOWTO: Generate a Message Graph

To generate a message graph using RecordFlux, locate the message spec and enter the following into the terminal window:

`rflx graph -d ./out dccp.rflx`

For the above, **out** is the output directory for the graph and **dccp.rflx** is the spec file.

## HOWTO: Generate Code Files from a Specification

To generate code files for the RecordFlux specification, locate the message spec and enter the following into the terminal window:

`rflx generate -d ./generated/ dccp.rflx`

For the above, **generated** is the output directory for the generated code files and **dccp.rflx** is the spec file.

## HOWTO: Validate the Specification

RecordFlux provides the ability to validate a message specification with real world files in a RAW format. To run validate and generate a corresponding report, locate the message spec and enter the following into the terminal window:

`rflx validate --coverage -v ./test/valid/ dccp.rflx DCCP::Header`

For the above, **./test/valid** is the folder in which "valid" sample RAW files are located and **dccp.rflx** is the spec file.
28 changes: 28 additions & 0 deletions examples/apps/dccp/build.gpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
aggregate project Build is

for Project_Files use ("dccp_client/dccp_client.gpr", "dccp_server/dccp_server.gpr");
for Create_Missing_Dirs use "True";

Proof_Switches :=
(
"--prover=z3,cvc5,altergo,colibri",
"--steps=0",
"--timeout=300",
"--memlimit=5000",
"--checks-as-errors",
"--warnings=error",
"--function-sandboxing=off",
"--counterexamples=off",
"--output=oneline",
"-j0"
);

package Prove is
for Proof_Switches ("Ada") use Proof_Switches;
end Prove;

package Builder is
for Global_Configuration_Pragmas use "common.adc";
end Builder;

end Build;
1 change: 1 addition & 0 deletions examples/apps/dccp/common.adc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pragma SPARK_Mode (On);
94 changes: 94 additions & 0 deletions examples/apps/dccp/common/socket.adb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
with Ada.Streams;

package body Socket with
SPARK_Mode => Off
is
function Is_Open (Chan : Channel) return Boolean is (Chan.Is_Open);

function Initialize
(Port : Natural; Server : Boolean := False) return Channel
is
Socket : GNAT.Sockets.Socket_Type;
begin
GNAT.Sockets.Create_Socket
(Socket => Socket, Mode => GNAT.Sockets.Socket_Datagram);

GNAT.Sockets.Set_Socket_Option
(Socket => Socket, Level => GNAT.Sockets.IP_Protocol_For_IP_Level,
Option => (GNAT.Sockets.Reuse_Address, True));

if Server then
GNAT.Sockets.Bind_Socket
(Socket => Socket,
Address =>
(Family => GNAT.Sockets.Family_Inet,
Addr => GNAT.Sockets.Inet_Addr ("127.0.0.1"),
Port => GNAT.Sockets.Port_Type (Port)));
end if;

return
Channel'
(Socket => Socket, Port => GNAT.Sockets.Port_Type (Port),
Is_Open => True);
end Initialize;

procedure Receive
(Chan : Channel; Data : out RFLX.RFLX_Types.Bytes;
Last : out RFLX.RFLX_Types.Index; Success : out Boolean)
is
Recv_Data : Ada.Streams.Stream_Element_Array (1 .. 4_096);
Recv_Last : Ada.Streams.Stream_Element_Offset;
From : GNAT.Sockets.Sock_Addr_Type;
use type Ada.Streams.Stream_Element_Offset;
begin
Success := False;

GNAT.Sockets.Receive_Socket
(Socket => Chan.Socket, Item => Recv_Data, Last => Recv_Last,
From => From);

for I in Recv_Data'First .. Recv_Last loop
declare
J : constant RFLX.RFLX_Types.Index :=
Data'First + RFLX.RFLX_Types.Index (I) -
RFLX.RFLX_Types.Index (Recv_Data'First);
begin
if J not in Data'Range then
return;
end if;
Data (J) := RFLX.RFLX_Types.Byte (Recv_Data (I));
end;
end loop;

Last := Data'First + RFLX.RFLX_Types.Index (Recv_Last - Recv_Data'First);
Success := True;
end Receive;

procedure Close (Chan : in out Channel) is
begin
GNAT.Sockets.Close_Socket (Chan.Socket);
Chan.Is_Open := False;
end Close;

procedure Send (Chan : Channel; Data : RFLX.RFLX_Types.Bytes) is
Last : Ada.Streams.Stream_Element_Offset;
Address : GNAT.Sockets.Sock_Addr_Type;
Send_Data : Ada.Streams.Stream_Element_Array (1 .. Data'Length);
use type Ada.Streams.Stream_Element_Offset;
begin

for I in Data'Range loop
Send_Data
(Send_Data'First +
Ada.Streams.Stream_Element_Offset (I - Data'First)) :=
Ada.Streams.Stream_Element (Data (I));
end loop;

Address.Port := Chan.Port;
Address.Addr := GNAT.Sockets.Inet_Addr ("127.0.0.1");

GNAT.Sockets.Send_Socket
(Socket => Chan.Socket, Item => Send_Data, Last => Last,
To => Address);
end Send;
end Socket;
40 changes: 40 additions & 0 deletions examples/apps/dccp/common/socket.ads
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
with RFLX.RFLX_Types;
private with GNAT.Sockets;

package Socket with
SPARK_Mode
is
type Channel is private with
Annotate => (GNATprove, Ownership, "Needs_Reclamation");
use type RFLX.RFLX_Types.Index;

function Is_Open (Chan : Channel) return Boolean with
Ghost, Annotate => (GNATprove, Ownership, "Needs_Reclamation");

function Initialize
(Port : Natural; Server : Boolean := False) return Channel with
Post => Is_Open (Initialize'Result);

procedure Receive
(Chan : Channel; Data : out RFLX.RFLX_Types.Bytes;
Last : out RFLX.RFLX_Types.Index; Success : out Boolean) with
Pre => Is_Open (Chan), Post => Data'First <= Last and Last <= Data'Last;

procedure Send (Chan : Channel; Data : RFLX.RFLX_Types.Bytes) with
Pre => Is_Open (Chan);

procedure Close (Chan : in out Channel) with
Pre => Is_Open (Chan), Post => not Is_Open (Chan),
Depends => (Chan => null, null => Chan);

private

pragma SPARK_Mode (Off);

type Channel is record
Socket : GNAT.Sockets.Socket_Type;
Port : GNAT.Sockets.Port_Type;
Is_Open : Boolean;
end record;

end Socket;
1 change: 1 addition & 0 deletions examples/apps/dccp/dccp_client/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/obj/**
25 changes: 25 additions & 0 deletions examples/apps/dccp/dccp_client/dccp_client.gpr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
with "../defaults";

project DCCP_Client is

for Languages use ("RecordFlux", "Ada");
for Source_Dirs use ("src", "../rflx/generated", "../rflx/specs", "../common");
for Object_Dir use "obj";
for Create_Missing_Dirs use "True";
for Main use ("dccp_client.adb");

package Compiler is
for Driver ("recordflux") use "";
for Default_Switches("Ada") use Defaults.Compiler_Switches;
end Compiler;

package Naming is
for Spec_Suffix ("recordflux") use ".rflx";
end Naming;

package Recordflux is
for Output_Dir use "../rflx/generated";
end Recordflux;

end DCCP_Client;

18 changes: 18 additions & 0 deletions examples/apps/dccp/dccp_client/src/dccp_client.adb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
with Socket;
with Msg_Write;

procedure DCCP_Client with
SPARK_Mode => On
is
Channel : Socket.Channel := Socket.Initialize (1234);
begin
Msg_Write.Send_Request (Channel);
Msg_Write.Send_Response (Channel);
Msg_Write.Send_Ack (Channel);
Msg_Write.Send_Data_Ack (Channel);
Msg_Write.Send_Data (Channel);
Msg_Write.Send_Close (Channel);
Msg_Write.Send_Reset (Channel);

Socket.Close (Channel);
end DCCP_Client;
Loading

0 comments on commit aa6d442

Please sign in to comment.