File : net-portability.adb


with Net.Portability.Linux, System, Ada.Exceptions, Ada.Unchecked_Conversion;
use Net.Portability.Linux, System, Ada.Exceptions;
with Debug, Unchecked_Conversion;

with Interfaces.C, Interfaces.C.Strings;
use Interfaces.C, Interfaces.C.Strings;

package body Net.Portability is
   function Get_Debug return Debug_Mode;
   pragma Inline (Get_Debug);
   
   function Get_Debug return Debug_Mode is
   begin
      if do_debug /= Inherit then return do_debug;
      else                        return Net.do_debug;
      end if;
   end Get_Debug;
   
   subtype void_p is System.Address;

   type iovec is record
      iov_base : void_p;
      iov_len  : size_t;
   end record;
   pragma Pack (iovec);
   type iovec_p is access all iovec;
   pragma Convention (C, iovec_p);
   
   type msghdr is record
      msg_name    : void_p;
      msg_namelen : socklen_t;
      msg_iov     : iovec_p;
      msg_iovlen  : socklen_t;
      msg_control : void_p;
      msg_controllen : socklen_t;
      msg_flags   : int;
   end record;
   pragma Pack (msghdr);
   type msghdr_p is access all msghdr;
   pragma Convention (C, msghdr_p);
   
   type hw_addr is array (1 .. 6) of Integer_8;
   pragma Convention (C, hw_addr);
   pragma Pack (hw_addr);

   type sockaddr_ll is record
      sll_family   : unsigned_short := PF_PACKET;
      sll_protocol : unsigned_short;
      sll_ifindex  : int;
      sll_hatype   : unsigned_short;
      sll_pkttype  : unsigned_char;
      sll_halen    : unsigned_char;
      sll_addr     : hw_addr;
   end record;
   pragma Pack (sockaddr_ll);
   type sockaddr_ll_p is access all sockaddr_ll;
   pragma Convention (C, sockaddr_ll_p);
   
   function socket (domain  : int;
                    typ      : int;
                    protocol : int) return int;
   function bind (socket  : int;
                  address : sockaddr_ll_p;
                  addrlen : int) return int;
   function recvmsg (socket : int;
                     msg    : msghdr_p;
                     flags  : int) return int;
   pragma Import (C, socket, "socket");
   pragma Import (C, bind, "bind");
   pragma Import (C, recvmsg, "recvmsg");


   --  Convert tools

   function to_word (i :  Two_Int_8) return Integer_16 is
      function To_16 is new Unchecked_Conversion (Two_Int_8, Integer_16);
   begin
      if Default_Bit_Order = High_Order_First then
         return To_16 (i);
      else
         return To_16 ((i (2), i (1)));
      end if;
   end to_word;
   
   function to_dword (i : Four_Int_8) return Integer_32 is
      function To_32 is new Unchecked_Conversion (Four_Int_8, Integer_32);
   begin
      if Default_Bit_Order = High_Order_First then
         return To_32 (i);
      else
         return To_32 ((i (4), i (3), i (2), i (1)));
      end if;
   end to_dword;
   
   function from_word (i : Integer_16) return  Two_Int_8 is
      function From_16 is new Unchecked_Conversion (Integer_16, Two_Int_8);
      r : Two_Int_8 := From_16 (i);
   begin
      if Default_Bit_Order = High_Order_First then
         return r;
      else
         return (r (2), r (1));
      end if;
   end from_word;
   
   function from_dword (i : Integer_32) return Four_Int_8 is
      function From_32 is new Unchecked_Conversion (Integer_32, Four_Int_8);
      r : Four_Int_8 := From_32 (i);
   begin
      if Default_Bit_Order = High_Order_First then
         return r;
      else
         return (r (4), r (3), r (2), r (1));
      end if;
   end from_dword;
   
   --  C Error handling
 
   procedure raise_C_error (e : in Exception_Id; message : in String);
   procedure raise_C_error (e : in Exception_Id; message : in String) is
      errno : int;
      pragma Import (C, errno, "errno");
      function strerror (errnum : int) return chars_ptr;
      pragma Import (C, strerror, "strerror");
      m : String := message & ": " & To_Ada (Value (strerror (errno)));
   begin
      Raise_Exception (e, m);
   end raise_C_error;

   --  Real work starts here
   
   function open_interface (i : Natural) return Interface is
      function To_16 is new Unchecked_Conversion (Two_Int_8, Integer_16);
      itf : Interface := socket (PF_PACKET, SOCK_RAW,
              int (To_16 (from_word (Integer_16 (ETH_P_ALL)))));
   begin
      if itf = int (-1) then
         raise_C_error (Open_Error'Identity, "socket");
      end if;
      
      if i > 0 then
         declare
            adr : aliased sockaddr_ll := (sll_ifindex  => int (i),
                                          sll_family   => PF_PACKET,
                                          sll_protocol => 0,
                                          sll_hatype   => 0,
                                          sll_pkttype  => 0,
                                          sll_addr     => (others => 0),
                                          sll_halen    => 8);
            b : int := bind (itf, adr'Unchecked_Access, adr'Size / 8);
         begin
            if b = int (-1) then
               raise_C_error (Open_Error'Identity,
                              "bind" & Natural'Image (adr'Size / 8));
            end if;
         end;
      end if;
      
      return itf;
   end open_interface;

   --  should be a pointer to the network card memory
   data_buffer    : aliased System.Storage_Elements.Storage_Array (1 .. 2000);
   control_buffer : aliased System.Storage_Elements.Storage_Array (1 .. 2000);
   
   function Receive_Blob (i : Interface) return Payload_Type is
      io   : aliased iovec  := (iov_base => data_buffer'Address,
                                iov_len  => data_buffer'Size / 8);
      m    : aliased msghdr := (msg_name => Null_Address,
                                msg_namelen => 0,
                                msg_iov  => io'Unchecked_Access,
                                msg_iovlen  => 1,
                                msg_control    => control_buffer'Address,
                                msg_controllen => control_buffer'Length,
                                msg_flags => 0);
      len  : int := recvmsg (i, m'Unchecked_Access, 0);
   begin
      if int (-1) = len then
         raise_C_error (Read_Error'Identity, "recvmsg");
      end if;
      return (data => data_buffer'Address,
            length => Integer_32 (len));
   end Receive_Blob;
end Net.Portability;