1--
2--  Copyright (C) 2011, 2012 secunet Security Networks AG
3--  Copyright (C) 2011, 2012 Reto Buerki <reet@codelabs.ch>
4--  Copyright (C) 2011, 2012 Adrian-Ken Rueegsegger <ken@codelabs.ch>
5--
6--  This program is free software; you can redistribute it and/or modify it
7--  under the terms of the GNU General Public License as published by the
8--  Free Software Foundation; either version 2 of the License, or (at your
9--  option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
10--
11--  This program is distributed in the hope that it will be useful, but
12--  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13--  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14--  for more details.
15--
16--  As a special exception, if other files instantiate generics from this
17--  unit,  or  you  link  this  unit  with  other  files  to  produce  an
18--  executable   this  unit  does  not  by  itself  cause  the  resulting
19--  executable to  be  covered by the  GNU General  Public License.  This
20--  exception does  not  however  invalidate  any  other reasons why  the
21--  executable file might be covered by the GNU Public License.
22--
23
24with Anet.Byte_Swapping;
25with Anet.Constants;
26with Anet.Util;
27
28package body Anet.UDP is
29
30   use Ada.Streams;
31
32   type Raw_UDP_Hdr_Type is record
33      Source : Double_Byte;
34      Dest   : Double_Byte;
35      Len    : Double_Byte;
36      Check  : Double_Byte;
37   end record;
38   --  Raw UDP header.
39
40   for Raw_UDP_Hdr_Type use record
41      Source at 0 range 0 .. 15;
42      Dest   at 2 range 0 .. 15;
43      Len    at 4 range 0 .. 15;
44      Check  at 6 range 0 .. 15;
45   end record;
46
47   for Raw_UDP_Hdr_Type'Size use 64;
48
49   subtype Raw_UDP_Hdr_Buffer_Type is
50     Stream_Element_Array (1 .. UDP_Header_Length);
51
52   type Pseudo_Hdr_Type is record
53      Source_Address : IPv4_Addr_Type;
54      Dest_Address   : IPv4_Addr_Type;
55      Reserved       : Byte;
56      Protocol       : Byte;
57      Length         : Double_Byte;
58   end record;
59   --  Pseudo header used for checksum calculation/verification.
60
61   for Pseudo_Hdr_Type use record
62      Source_Address at  0 range 0 .. 31;
63      Dest_Address   at  4 range 0 .. 31;
64      Reserved       at  8 range 0 .. 7;
65      Protocol       at  9 range 0 .. 7;
66      Length         at 10 range 0 .. 15;
67   end record;
68
69   for Pseudo_Hdr_Type'Size use 96;
70   for Pseudo_Hdr_Type'Alignment use 1;
71
72   subtype Pseudo_Buffer_Type is Stream_Element_Array
73     (1 .. Pseudo_Hdr_Type'Object_Size / Stream_Element'Object_Size);
74
75   function Compute_Checksum
76     (Hdr_Buffer : Raw_UDP_Hdr_Buffer_Type;
77      Payload    : Ada.Streams.Stream_Element_Array;
78      Src_IP     : IPv4_Addr_Type;
79      Dst_IP     : IPv4_Addr_Type)
80      return Double_Byte;
81   --  Calculate UDP checksum for given UDP header and payload.
82
83   -------------------------------------------------------------------------
84
85   function Compute_Checksum
86     (Hdr_Buffer : Raw_UDP_Hdr_Buffer_Type;
87      Payload    : Ada.Streams.Stream_Element_Array;
88      Src_IP     : IPv4_Addr_Type;
89      Dst_IP     : IPv4_Addr_Type)
90      return Double_Byte
91   is
92      Pseudo_Buffer : Pseudo_Buffer_Type;
93      Pseudo_Hdr    : Pseudo_Hdr_Type;
94      for Pseudo_Hdr'Address use Pseudo_Buffer'Address;
95
96      subtype Pseudo_Hdr_Idx is Stream_Element_Offset range
97        1 .. Pseudo_Buffer'Length;
98
99      subtype UDP_Hdr_Idx is Stream_Element_Offset range
100        Pseudo_Hdr_Idx'Last + 1 .. Pseudo_Hdr_Idx'Last + Hdr_Buffer'Length;
101
102      Chksum_Buffer : Stream_Element_Array
103        (1 .. Pseudo_Buffer'Length + Hdr_Buffer'Length + Payload'Length);
104   begin
105      Pseudo_Hdr.Source_Address := Src_IP;
106      Pseudo_Hdr.Dest_Address   := Dst_IP;
107      Pseudo_Hdr.Reserved       := 0;
108      Pseudo_Hdr.Protocol       := Constants.Sys.IPPROTO_UDP;
109      Pseudo_Hdr.Length         := Byte_Swapping.Host_To_Network
110        (Input => Payload'Length + Hdr_Buffer'Length);
111
112      Chksum_Buffer (Pseudo_Hdr_Idx'Range) := Pseudo_Buffer;
113      Chksum_Buffer (UDP_Hdr_Idx'Range)    := Hdr_Buffer;
114      Chksum_Buffer (UDP_Hdr_Idx'Last + 1 .. Chksum_Buffer'Last) := Payload;
115
116      return Util.Calculate_One_Complement (Data => Chksum_Buffer);
117   end Compute_Checksum;
118
119   -------------------------------------------------------------------------
120
121   function Create_Header
122     (Payload  : Ada.Streams.Stream_Element_Array;
123      Src_IP   : IPv4_Addr_Type;
124      Dst_IP   : IPv4_Addr_Type;
125      Src_Port : Port_Type;
126      Dst_Port : Port_Type)
127      return Ada.Streams.Stream_Element_Array
128   is
129      Hdr_Buffer : Raw_UDP_Hdr_Buffer_Type;
130      for Hdr_Buffer'Alignment use 16;
131
132      UDP_Hdr : Raw_UDP_Hdr_Type;
133      for UDP_Hdr'Address use Hdr_Buffer'Address;
134   begin
135      UDP_Hdr.Check  := 0;
136      UDP_Hdr.Source := Byte_Swapping.Host_To_Network
137        (Input => Double_Byte (Src_Port));
138      UDP_Hdr.Dest   := Byte_Swapping.Host_To_Network
139        (Input => Double_Byte (Dst_Port));
140      UDP_Hdr.Len    := Byte_Swapping.Host_To_Network
141        (Input => Payload'Length + Hdr_Buffer'Length);
142      UDP_Hdr.Check  := Byte_Swapping.Host_To_Network
143        (Input => Compute_Checksum
144           (Hdr_Buffer => Hdr_Buffer,
145            Payload    => Payload,
146            Src_IP     => Src_IP,
147            Dst_IP     => Dst_IP));
148
149      return Hdr_Buffer;
150   end Create_Header;
151
152   -------------------------------------------------------------------------
153
154   procedure Validate_Checksum
155     (Packet : Ada.Streams.Stream_Element_Array;
156      Src_IP : IPv4_Addr_Type;
157      Dst_IP : IPv4_Addr_Type)
158   is
159      Hdr_Buffer : Raw_UDP_Hdr_Buffer_Type;
160      for Hdr_Buffer'Alignment use 16;
161
162      UDP_Hdr : Raw_UDP_Hdr_Type;
163      for UDP_Hdr'Address use Hdr_Buffer'Address;
164
165      Chk_Pkt, Chk_Calc : Double_Byte;
166   begin
167      Hdr_Buffer := Packet
168        (Packet'First .. Packet'First + UDP_Header_Length - 1);
169
170      if UDP_Hdr.Check = 0 then
171         return;
172      end if;
173
174      Chk_Pkt := Byte_Swapping.Network_To_Host (Input => UDP_Hdr.Check);
175      UDP_Hdr.Check := 0;
176
177      Chk_Calc := Compute_Checksum
178        (Hdr_Buffer => Hdr_Buffer,
179         Payload    => Packet
180           (Packet'First + UDP_Header_Length .. Packet'Last),
181         Src_IP     => Src_IP,
182         Dst_IP     => Dst_IP);
183
184      if Chk_Calc /= Chk_Pkt then
185         raise Invalid_UDP_Packet with "UDP header checksum" & Chk_Pkt'Img
186           & " invalid, should be" & Chk_Calc'Img;
187      end if;
188   end Validate_Checksum;
189
190end Anet.UDP;
191