-----------------------------------------------------------------------
-- net-buffers -- Network buffers
-- Copyright (C) 2016, 2017, 2018 Stephane Carrez
-- Written by Stephane Carrez (Stephane.Carrez@gmail.com)
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-----------------------------------------------------------------------
with Ada.Unchecked_Conversion;
package body Net.Buffers is
ETHER_POS : constant Uint16 := 0;
IP_POS : constant Uint16 := ETHER_POS + 14;
UDP_POS : constant Uint16 := IP_POS + 20; -- Note: this is wrong due to IP options.
-- TCP_POS : constant Uint16 := IP_POS + 24; -- Note: this is wrong due to IP options.
IGMP_POS : constant Uint16 := IP_POS + 24;
ICMP_POS : constant Uint16 := IP_POS + 20;
DHCP_POS : constant Uint16 := IP_POS + 20 + 8;
-- DATA_POS : constant Natural := UDP_POS + 8;
type Offset_Table is array (Packet_Type) of Uint16;
Offsets : constant Offset_Table :=
(RAW_PACKET => 0,
ETHER_PACKET => 14,
ARP_PACKET => 14 + 8,
IP_PACKET => 14 + 20,
ICMP_PACKET => 14 + 20 + 8,
UDP_PACKET => 14 + 20 + 8,
DHCP_PACKET => 14 + 20 + 8 + 236);
function As_Ethernet is
new Ada.Unchecked_Conversion (Source => System.Address,
Target => Net.Headers.Ether_Header_Access);
function As_Arp is
new Ada.Unchecked_Conversion (Source => System.Address,
Target => Net.Headers.Arp_Packet_Access);
function As_Ip_Header is
new Ada.Unchecked_Conversion (Source => System.Address,
Target => Net.Headers.IP_Header_Access);
function As_Udp_Header is
new Ada.Unchecked_Conversion (Source => System.Address,
Target => Net.Headers.UDP_Header_Access);
function As_Tcp_Header is
new Ada.Unchecked_Conversion (Source => System.Address,
Target => Net.Headers.TCP_Header_Access);
function As_Igmp_Header is
new Ada.Unchecked_Conversion (Source => System.Address,
Target => Net.Headers.IGMP_Header_Access);
function As_Icmp_Header is
new Ada.Unchecked_Conversion (Source => System.Address,
Target => Net.Headers.ICMP_Header_Access);
function As_Dhcp_Header is
new Ada.Unchecked_Conversion (Source => System.Address,
Target => Net.Headers.DHCP_Header_Access);
protected Manager with Priority => Net.Network_Priority is
procedure Allocate (Packet : out Packet_Buffer_Access);
procedure Release (Packet : in out Packet_Buffer_Access);
procedure Add_Region (Addr : in System.Address;
Count : in Uint32);
procedure Release (List : in out Buffer_List);
procedure Allocate (List : in out Buffer_List;
Count : in Natural);
private
Free_List : Packet_Buffer_Access;
end Manager;
-- ------------------------------
-- Returns true if the buffer is null (allocation failed).
-- ------------------------------
function Is_Null (Buf : in Buffer_Type) return Boolean is
begin
return Buf.Packet = null;
end Is_Null;
-- ------------------------------
-- Allocate a buffer from the pool. No exception is raised if there is no available buffer.
-- The Is_Null operation must be used to check the buffer allocation.
-- ------------------------------
procedure Allocate (Buf : out Buffer_Type) is
begin
Manager.Allocate (Buf.Packet);
Buf.Size := 0;
end Allocate;
-- ------------------------------
-- Release the buffer back to the pool.
-- ------------------------------
procedure Release (Buf : in out Buffer_Type) is
begin
if Buf.Packet /= null then
Manager.Release (Buf.Packet);
end if;
end Release;
-- ------------------------------
-- Transfer the ownership of the buffer from From to To.
-- If the destination has a buffer, it is first released.
-- ------------------------------
procedure Transfer (To : in out Buffer_Type;
From : in out Buffer_Type) is
begin
if To.Packet /= null then
Manager.Release (To.Packet);
end if;
To.Packet := From.Packet;
To.Size := From.Size;
From.Packet := null;
end Transfer;
-- ------------------------------
-- Switch the ownership of the two buffers. The typical usage is on the Ethernet receive
-- ring to peek a received packet and install a new buffer on the ring so that there is
-- always a buffer on the ring.
-- ------------------------------
procedure Switch (To : in out Buffer_Type;
From : in out Buffer_Type) is
Size : constant Uint16 := To.Size;
Packet : constant Packet_Buffer_Access := To.Packet;
begin
To.Size := From.Size;
To.Packet := From.Packet;
From.Size := Size;
From.Packet := Packet;
end Switch;
function Get_Data_Address (Buf : in Buffer_Type) return System.Address is
begin
return Buf.Packet.Data (Buf.Packet.Data'First)'Address;
end Get_Data_Address;
function Get_Data_Size (Buf : in Buffer_Type;
Kind : in Packet_Type) return Uint16 is
begin
if Buf.Size = 0 then
return Buf.Pos - Offsets (Kind);
else
return Buf.Size - Offsets (Kind);
end if;
end Get_Data_Size;
procedure Set_Data_Size (Buf : in out Buffer_Type; Size : in Uint16) is
begin
Buf.Pos := Size + Offsets (Buf.Kind);
Buf.Size := 0;
end Set_Data_Size;
function Get_Length (Buf : in Buffer_Type) return Uint16 is
begin
return Buf.Size;
end Get_Length;
procedure Set_Length (Buf : in out Buffer_Type; Size : in Uint16) is
begin
Buf.Size := Size;
Buf.Packet.Size := Size;
end Set_Length;
-- ------------------------------
-- Set the packet type.
-- ------------------------------
procedure Set_Type (Buf : in out Buffer_Type;
Kind : in Packet_Type) is
begin
Buf.Kind := Kind;
Buf.Pos := Offsets (Kind);
end Set_Type;
-- ------------------------------
-- Add a byte to the buffer data, moving the buffer write position.
-- ------------------------------
procedure Put_Uint8 (Buf : in out Buffer_Type;
Value : in Net.Uint8) is
begin
Buf.Packet.Data (Buf.Pos) := Value;
Buf.Pos := Buf.Pos + 1;
end Put_Uint8;
-- ------------------------------
-- Add a 16-bit value in network byte order to the buffer data,
-- moving the buffer write position.
-- ------------------------------
procedure Put_Uint16 (Buf : in out Buffer_Type;
Value : in Net.Uint16) is
begin
Buf.Packet.Data (Buf.Pos) := Net.Uint8 (Interfaces.Shift_Right (Value, 8));
Buf.Packet.Data (Buf.Pos + 1) := Net.Uint8 (Value and 16#0ff#);
Buf.Pos := Buf.Pos + 2;
end Put_Uint16;
-- ------------------------------
-- Add a 32-bit value in network byte order to the buffer data,
-- moving the buffer write position.
-- ------------------------------
procedure Put_Uint32 (Buf : in out Buffer_Type;
Value : in Net.Uint32) is
begin
Buf.Packet.Data (Buf.Pos) := Net.Uint8 (Interfaces.Shift_Right (Value, 24));
Buf.Packet.Data (Buf.Pos + 1) := Net.Uint8 (Interfaces.Shift_Right (Value, 16) and 16#0ff#);
Buf.Packet.Data (Buf.Pos + 2) := Net.Uint8 (Interfaces.Shift_Right (Value, 8) and 16#0ff#);
Buf.Packet.Data (Buf.Pos + 3) := Net.Uint8 (Value and 16#0ff#);
Buf.Pos := Buf.Pos + 4;
end Put_Uint32;
-- ------------------------------
-- Add a string to the buffer data, moving the buffer write position.
-- When With_Null is set, a NUL byte is added after the string.
-- ------------------------------
procedure Put_String (Buf : in out Buffer_Type;
Value : in String;
With_Null : in Boolean := False) is
Pos : Uint16 := Buf.Pos;
begin
for C of Value loop
Buf.Packet.Data (Pos) := Character'Pos (C);
Pos := Pos + 1;
end loop;
if With_Null then
Buf.Packet.Data (Pos) := 0;
Pos := Pos + 1;
end if;
Buf.Pos := Pos;
end Put_String;
-- ------------------------------
-- Add an IP address to the buffer data, moving the buffer write position.
-- ------------------------------
procedure Put_Ip (Buf : in out Buffer_Type;
Value : in Ip_Addr) is
Pos : Uint16 := Buf.Pos;
begin
for C of Value loop
Buf.Packet.Data (Pos) := C;
Pos := Pos + 1;
end loop;
Buf.Pos := Pos;
end Put_Ip;
-- ------------------------------
-- Get a byte from the buffer, moving the buffer read position.
-- ------------------------------
function Get_Uint8 (Buf : in out Buffer_Type) return Net.Uint8 is
Pos : constant Net.Uint16 := Buf.Pos;
begin
Buf.Pos := Pos + 1;
return Buf.Packet.Data (Pos);
end Get_Uint8;
-- ------------------------------
-- Get a 16-bit value in network byte order from the buffer, moving the buffer read position.
-- ------------------------------
function Get_Uint16 (Buf : in out Buffer_Type) return Net.Uint16 is
Pos : constant Net.Uint16 := Buf.Pos;
begin
Buf.Pos := Pos + 2;
return Interfaces.Shift_Left (Net.Uint16 (Buf.Packet.Data (Pos)), 8)
or Net.Uint16 (Buf.Packet.Data (Pos + 1));
end Get_Uint16;
-- ------------------------------
-- Get a 32-bit value in network byte order from the buffer, moving the buffer read position.
-- ------------------------------
function Get_Uint32 (Buf : in out Buffer_Type) return Net.Uint32 is
Pos : constant Net.Uint16 := Buf.Pos;
begin
Buf.Pos := Pos + 4;
return Interfaces.Shift_Left (Net.Uint32 (Buf.Packet.Data (Pos)), 24)
or Interfaces.Shift_Left (Net.Uint32 (Buf.Packet.Data (Pos + 1)), 16)
or Interfaces.Shift_Left (Net.Uint32 (Buf.Packet.Data (Pos + 2)), 8)
or Net.Uint32 (Buf.Packet.Data (Pos + 3));
end Get_Uint32;
-- ------------------------------
-- Get an IPv4 value from the buffer, moving the buffer read position.
-- ------------------------------
function Get_Ip (Buf : in out Buffer_Type) return Net.Ip_Addr is
Pos : constant Net.Uint16 := Buf.Pos;
Result : Ip_Addr;
begin
Buf.Pos := Pos + 4;
Result (1) := Buf.Packet.Data (Pos);
Result (2) := Buf.Packet.Data (Pos + 1);
Result (3) := Buf.Packet.Data (Pos + 2);
Result (4) := Buf.Packet.Data (Pos + 3);
return Result;
end Get_Ip;
-- ------------------------------
-- Get a string whose length is specified by the target value.
-- ------------------------------
procedure Get_String (Buf : in out Buffer_Type;
Into : out String) is
Pos : Net.Uint16 := Buf.Pos;
begin
for I in Into'Range loop
Into (I) := Character'Val (Buf.Packet.Data (Pos));
Pos := Pos + 1;
end loop;
Buf.Pos := Pos;
end Get_String;
-- ------------------------------
-- Skip a number of bytes in the buffer, moving the buffer position Size bytes ahead.
-- ------------------------------
procedure Skip (Buf : in out Buffer_Type;
Size : in Net.Uint16) is
begin
Buf.Pos := Buf.Pos + Size;
end Skip;
-- ------------------------------
-- Get the number of bytes still available when reading the packet.
-- ------------------------------
function Available (Buf : in Buffer_Type) return Net.Uint16 is
begin
return Buf.Size - Buf.Pos;
end Available;
-- ------------------------------
-- Get access to the Ethernet header.
-- ------------------------------
function Ethernet (Buf : in Buffer_Type) return Net.Headers.Ether_Header_Access is
begin
return As_Ethernet (Buf.Packet.Data (Buf.Packet.Data'First)'Address);
end Ethernet;
-- ------------------------------
-- Get access to the ARP packet.
-- ------------------------------
function Arp (Buf : in Buffer_Type) return Net.Headers.Arp_Packet_Access is
begin
return As_Arp (Buf.Packet.Data (Buf.Packet.Data'First)'Address);
end Arp;
-- ------------------------------
-- Get access to the IPv4 header.
-- ------------------------------
function IP (Buf : in Buffer_Type) return Net.Headers.IP_Header_Access is
begin
return As_Ip_Header (Buf.Packet.Data (IP_POS)'Address);
end IP;
-- ------------------------------
-- Get access to the UDP header.
-- ------------------------------
function UDP (Buf : in Buffer_Type) return Net.Headers.UDP_Header_Access is
begin
return As_Udp_Header (Buf.Packet.Data (UDP_POS)'Address);
end UDP;
-- ------------------------------
-- Get access to the TCP header.
-- ------------------------------
function TCP (Buf : in Buffer_Type) return Net.Headers.TCP_Header_Access is
begin
return As_Tcp_Header (Buf.Packet.Data (20 + 14 + 2)'Address);
end TCP;
-- ------------------------------
-- Get access to the IGMP header.
-- ------------------------------
function IGMP (Buf : in Buffer_Type) return Net.Headers.IGMP_Header_Access is
begin
return As_Igmp_Header (Buf.Packet.Data (IGMP_POS)'Address);
end IGMP;
-- ------------------------------
-- Get access to the ICMP header.
-- ------------------------------
function ICMP (Buf : in Buffer_Type) return Net.Headers.ICMP_Header_Access is
begin
return As_Icmp_Header (Buf.Packet.Data (ICMP_POS)'Address);
end ICMP;
-- ------------------------------
-- Get access to the DHCP header.
-- ------------------------------
function DHCP (Buf : in Buffer_Type) return Net.Headers.DHCP_Header_Access is
begin
return As_Dhcp_Header (Buf.Packet.Data (DHCP_POS)'Address);
end DHCP;
-- ------------------------------
-- Returns True if the list is empty.
-- ------------------------------
function Is_Empty (List : in Buffer_List) return Boolean is
begin
return List.Head = null;
end Is_Empty;
-- ------------------------------
-- Insert the buffer to the list.
-- ------------------------------
procedure Insert (Into : in out Buffer_List;
Buf : in out Buffer_Type) is
begin
if Into.Tail = null then
Into.Tail := Buf.Packet;
Buf.Packet.Next := null;
else
Buf.Packet.Next := Into.Head;
end if;
Into.Head := Buf.Packet;
Buf.Packet := null;
end Insert;
-- ------------------------------
-- Release all the buffers held by the list.
-- ------------------------------
procedure Release (List : in out Buffer_List) is
begin
Manager.Release (List);
end Release;
-- ------------------------------
-- Allocate Count buffers and add them to the list.
-- There is no guarantee that the required number of buffers will be allocated.
-- ------------------------------
procedure Allocate (List : in out Buffer_List;
Count : in Natural) is
begin
Manager.Allocate (List, Count);
end Allocate;
-- ------------------------------
-- Peek a buffer from the list.
-- ------------------------------
procedure Peek (From : in out Buffer_List;
Buf : in out Buffer_Type) is
begin
Buf.Packet := From.Head;
Buf.Size := Buf.Packet.Size;
From.Head := From.Head.Next;
if From.Head = null then
From.Tail := null;
end if;
end Peek;
-- ------------------------------
-- Transfer the list of buffers held by From at end of the list held
-- by To. After the transfer, the From list is empty.
-- The complexity is in O(1).
-- ------------------------------
procedure Transfer (To : in out Buffer_List;
From : in out Buffer_List) is
begin
if To.Tail /= null then
To.Tail.Next := From.Head;
From.Head := To.Head;
else
To.Tail := From.Tail;
To.Head := From.Head;
end if;
From.Head := null;
From.Tail := null;
end Transfer;
-- ------------------------------
-- Add a memory region to the buffer pool.
-- ------------------------------
procedure Add_Region (Addr : in System.Address;
Size : in Uint32) is
Count : constant Uint32 := Size / NET_ALLOC_SIZE;
begin
Manager.Add_Region (Addr, Count);
end Add_Region;
protected body Manager is
procedure Allocate (Packet : out Packet_Buffer_Access) is
begin
Packet := Free_List;
if Packet /= null then
Free_List := Packet.Next;
Packet.Size := 0;
end if;
end Allocate;
procedure Allocate (List : in out Buffer_List;
Count : in Natural) is
Packet : Packet_Buffer_Access;
begin
for I in 1 .. Count loop
exit when Free_List = null;
Packet := Free_List;
Free_List := Packet.Next;
if List.Tail = null then
List.Tail := Packet;
else
Packet.Next := List.Head;
end if;
List.Head := Packet;
end loop;
end Allocate;
procedure Release (Packet : in out Packet_Buffer_Access) is
begin
Packet.Next := Free_List;
Free_List := Packet;
Packet := null;
end Release;
procedure Release (List : in out Buffer_List) is
begin
List.Tail.Next := Free_List;
Free_List := List.Head;
List.Head := null;
List.Tail := null;
end Release;
procedure Add_Region (Addr : in System.Address;
Count : in Uint32) is
type Packet_Array is array (1 .. Count) of aliased Packet_Buffer;
type Packet_Array_Access is access all Packet_Array;
function As_Packet_List is
new Ada.Unchecked_Conversion (Source => System.Address,
Target => Packet_Array_Access);
Packets : Packet_Array_Access := As_Packet_List (Addr);
begin
for I in 1 .. Count loop
Packets (I).Next := Free_List;
Free_List := Packets (I)'Unchecked_Access;
end loop;
end Add_Region;
end Manager;
end Net.Buffers;