000fe3966381c15393074d029408a5326ce31e30
[tpg/acess2.git] / KernelLand / Modules / IPStack / ipv4.c
1 /*
2  * Acess2 IP Stack
3  * - IPv4 Protcol Handling
4  */
5 #define DEBUG   0
6 #include "ipstack.h"
7 #include "link.h"
8 #include "ipv4.h"
9 #include "hwaddr_cache.h"
10 #include "firewall.h"
11
12 // === CONSTANTS ===
13 #define DEFAULT_TTL     32
14 #define IPV4_TRACE      1       // set to 1 to enable packet tracing
15
16 // === IMPORTS ===
17 extern tInterface       *gIP_Interfaces;
18 extern void     ICMP_Initialise();
19 extern  int     ICMP_Ping(tInterface *Interface, tIPv4 Addr);
20
21 // === PROTOTYPES ===
22  int    IPv4_Initialise();
23  int    IPv4_RegisterCallback(int ID, tIPCallback Callback);
24 void    IPv4_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer);
25 tInterface      *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast);
26 Uint32  IPv4_Netmask(int FixedBits);
27 Uint16  IPv4_Checksum(const void *Buf, size_t Length);
28  int    IPv4_Ping(tInterface *Iface, tIPv4 Addr);
29
30 // === GLOBALS ===
31 tIPCallback     gaIPv4_Callbacks[256];
32
33 // === CODE ===
34 /**
35  * \brief Initialise the IPv4 Code
36  */
37 int IPv4_Initialise()
38 {
39         ICMP_Initialise();
40         Link_RegisterType(IPV4_ETHERNET_ID, IPv4_int_GetPacket);
41         return 1;
42 }
43
44 /**
45  * \brief Registers a callback
46  * \param ID    8-bit packet type ID
47  * \param Callback      Callback function
48  */
49 int IPv4_RegisterCallback(int ID, tIPCallback Callback)
50 {
51         if( ID < 0 || ID > 255 )        return 0;
52         if( gaIPv4_Callbacks[ID] )      return 0;
53         gaIPv4_Callbacks[ID] = Callback;
54         return 1;
55 }
56
57 /**
58  * \brief Creates and sends an IPv4 Packet
59  * \param Iface Interface
60  * \param Address       Destination IP
61  * \param Protocol      Protocol ID
62  * \param ID    Some random ID number
63  * \param Length        Data Length
64  * \param Data  Packet Data
65  * \return Boolean Success
66  */
67 int IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, tIPStackBuffer *Buffer)
68 {
69         tMacAddr        to;
70         tIPv4Header     hdr;
71          int    length;
72
73         length = IPStack_Buffer_GetLength(Buffer);
74         
75         // --- Resolve destination MAC address
76         to = HWCache_Resolve(Iface, &Address);
77         if( MAC_EQU(to, cMAC_ZERO) ) {
78                 // No route to host
79                 Log_Notice("IPv4", "No route to host %i.%i.%i.%i",
80                         Address.B[0], Address.B[1], Address.B[2], Address.B[3]);
81                 return 0;
82         }
83         
84         // --- Handle OUTPUT firewall rules
85         // TODO: Update firewall rules for tIPStackBuffer
86         #if 0
87         int ret = IPTables_TestChain("OUTPUT",
88                 4, (tIPv4*)Iface->Address, &Address,
89                 Protocol, 0,
90                 length, Data);
91         if(ret > 0) {
92                 // Just drop it (with an error)
93                 Log_Notice("IPv4", "Firewall dropped packet");
94                 return 0;
95         }
96         #endif
97
98         // --- Initialise header        
99         hdr.Version = 4;
100         hdr.HeaderLength = sizeof(tIPv4Header)/4;
101         hdr.DiffServices = 0;   // TODO: Check
102         
103         hdr.Reserved = 0;
104         hdr.DontFragment = 0;
105         hdr.MoreFragments = 0;
106         hdr.FragOffLow = 0;
107         hdr.FragOffHi = 0;
108         
109         hdr.TotalLength = htons( sizeof(tIPv4Header) + length );
110         hdr.Identifcation = htons( ID );        // TODO: Check
111         hdr.TTL = DEFAULT_TTL;
112         hdr.Protocol = Protocol;
113         hdr.HeaderChecksum = 0; // Will be set later
114         hdr.Source = *(tIPv4*)Iface->Address;
115         hdr.Destination = Address;
116
117         // Actually set checksum (zeroed above)
118         hdr.HeaderChecksum = htons( IPv4_Checksum(&hdr, sizeof(tIPv4Header)) );
119
120         IPStack_Buffer_AppendSubBuffer(Buffer, sizeof(tIPv4Header), 0, &hdr, NULL, NULL);
121
122         #if IPV4_TRACE
123         Log_Log("IPv4", "Sending packet to %i.%i.%i.%i",
124                 Address.B[0], Address.B[1], Address.B[2], Address.B[3]);
125         #endif
126         Link_SendPacket(Iface->Adapter, IPV4_ETHERNET_ID, to, Buffer);
127         return 1;
128 }
129
130 /**
131  * \fn void IPv4_int_GetPacket(tInterface *Adapter, tMacAddr From, int Length, void *Buffer)
132  * \brief Process an IPv4 Packet
133  */
134 void IPv4_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
135 {
136         tIPv4Header     *hdr = Buffer;
137         tInterface      *iface;
138         Uint8   *data;
139          int    dataLength;
140          int    ret;
141         
142         if(Length < sizeof(tIPv4Header))        return;
143         
144         #if 0
145         //Log_Log("IPv4", "Version = %i", hdr->Version);
146         //Log_Log("IPv4", "HeaderLength = %i", hdr->HeaderLength);
147         //Log_Log("IPv4", "DiffServices = %i", hdr->DiffServices);
148         Log_Debug("IPv4", "TotalLength = %i", ntohs(hdr->TotalLength) );
149         //Log_Log("IPv4", "Identifcation = %i", ntohs(hdr->Identifcation) );
150         //Log_Log("IPv4", "TTL = %i", hdr->TTL );
151         Log_Debug("IPv4", "Protocol = %i", hdr->Protocol );
152         //Log_Log("IPv4", "HeaderChecksum = 0x%x", ntohs(hdr->HeaderChecksum) );
153         Log_Debug("IPv4", "Source = %i.%i.%i.%i",
154                 hdr->Source.B[0], hdr->Source.B[1], hdr->Source.B[2], hdr->Source.B[3] );
155         Log_Debug("IPv4", "Destination = %i.%i.%i.%i",
156                 hdr->Destination.B[0], hdr->Destination.B[1],
157                 hdr->Destination.B[2], hdr->Destination.B[3] );
158         #endif  
159
160         // Check that the version IS IPv4
161         if(hdr->Version != 4) {
162                 Log_Log("IPv4", "hdr->Version(%i) != 4", hdr->Version);
163                 return;
164         }
165         
166         // Check Header checksum
167         {
168                 Uint16  hdrVal, compVal;
169                 hdrVal = ntohs(hdr->HeaderChecksum);
170                 hdr->HeaderChecksum = 0;
171                 compVal = IPv4_Checksum(hdr, hdr->HeaderLength * 4);
172                 if(hdrVal != compVal) {
173                         Log_Log("IPv4", "Header checksum fails (%04x != %04x)", hdrVal, compVal);
174                         return ;
175                 }
176                 hdr->HeaderChecksum = hdrVal;
177         }
178         
179         // Check Packet length
180         if( ntohs(hdr->TotalLength) > Length) {
181                 Log_Log("IPv4", "hdr->TotalLength(%i) > Length(%i)", ntohs(hdr->TotalLength), Length);
182                 return;
183         }
184         
185         // TODO: Handle packet fragmentation
186         
187         #if IPV4_TRACE
188         Log_Debug("IPv4", "Proto 0x%x From %i.%i.%i.%i to %i.%i.%i.%i",
189                 hdr->Protocol,
190                 hdr->Source.B[0], hdr->Source.B[1], hdr->Source.B[2], hdr->Source.B[3],
191                 hdr->Destination.B[0], hdr->Destination.B[1], hdr->Destination.B[2], hdr->Destination.B[3]
192                 );
193         #endif
194         
195         // Get Data and Data Length
196         dataLength = ntohs(hdr->TotalLength) - sizeof(tIPv4Header);
197         data = &hdr->Options[0];
198         
199         // Get Interface (allowing broadcasts)
200         iface = IPv4_GetInterface(Adapter, hdr->Destination, 1);
201         
202         // Firewall rules
203         if( iface ) {
204                 // Incoming Packets
205                 ret = IPTables_TestChain("INPUT",
206                         4, &hdr->Source, &hdr->Destination,
207                         hdr->Protocol, 0,
208                         dataLength, data
209                         );
210         }
211         else {
212                 // Routed packets
213                 // Drop the packet if the TTL is zero
214                 if( hdr->TTL == 0 ) {
215                         Log_Warning("IPv4", "TODO: Send ICMP-Timeout when TTL exceeded");
216                         return ;
217                 }
218                 hdr->TTL --;
219
220                 ret = IPTables_TestChain("FORWARD",
221                         4, &hdr->Source, &hdr->Destination,
222                         hdr->Protocol, 0,
223                         dataLength, data
224                         );
225         }
226         switch(ret)
227         {
228         // 0 - Allow
229         case 0: break;
230         // 1 - Silent Drop
231         case 1:
232                 Log_Debug("IPv4", "Silently dropping packet");
233                 return ;
234         case -1:
235                 // Bad rule
236                 break ;
237         // Unknown, silent drop
238         default:
239                 Log_Warning("IPv4", "Unknown firewall rule");
240                 return ;
241         }
242         
243         // Routing
244         if(!iface)
245         {
246                 //IPStack_RoutePacket(4, &hdr->Destination, Length, Buffer);
247                 return ;
248         }
249
250         // Populate ARP cache from recieved packets
251         // - Should be safe
252         if( IPStack_CompareAddress(4, &hdr->Source, iface->Address, iface->SubnetBits) )
253         {
254                 HWCache_Set(Adapter, 4, &hdr->Source, &From);
255         }
256         
257         // Send it on
258         if( !gaIPv4_Callbacks[hdr->Protocol] ) {
259                 Log_Log("IPv4", "Unknown Protocol %i", hdr->Protocol);
260                 return ;
261         }
262         
263         gaIPv4_Callbacks[hdr->Protocol]( iface, &hdr->Source, dataLength, data );
264 }
265
266 /**
267  * \fn tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address)
268  * \brief Searches an adapter for a matching address
269  * \param Adapter       Incoming Adapter
270  * \param Address       Destination Address
271  * \param Broadcast     Allow broadcast packets
272  */
273 tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast)
274 {
275         tInterface      *iface = NULL, *zero_iface = NULL;
276         Uint32  netmask;
277         Uint32  addr, this;
278
279         ENTER("pAdapter xAddress bBroadcast", Adapter, Address, Broadcast);     
280
281         addr = ntohl( Address.L );
282         LOG("addr = 0x%x", addr);
283         
284         for( iface = gIP_Interfaces; iface; iface = iface->Next)
285         {
286                 if( iface->Adapter != Adapter ) continue;
287                 if( iface->Type != 4 )  continue;
288                 if( IP4_EQU(Address, *(tIPv4*)iface->Address) ) {
289                         LOG("Exact match");
290                         LEAVE('p', iface);
291                         return iface;
292                 }
293
294                 LOG("iface->Address = 0x%x", *(Uint32*)iface->Address);
295
296                 if( *(Uint32*)iface->Address == 0 ) {
297                         if( zero_iface ) {
298                                 Log_Notice("IPv4", "Multiple 0.0.0.0 interfaces on the same adapter, ignoring");
299                         }
300                         else {
301                                 zero_iface = iface;
302                                 LOG("Zero IF %p", iface);
303                         }
304                         continue ;
305                 }               
306
307                 if( !Broadcast )        continue;
308                 
309                 // Check for broadcast
310                 this = ntohl( ((tIPv4*)iface->Address)->L );
311                 netmask = IPv4_Netmask(iface->SubnetBits);
312                 LOG("iface addr = 0x%x, netmask = 0x%x (bits = %i)", this, netmask, iface->SubnetBits);
313
314                 if( (addr & netmask) == (this & netmask) && (addr & ~netmask) == (0xFFFFFFFF & ~netmask) )
315                 {
316                         LOG("Broadcast match");
317                         LEAVE('p', iface);
318                         return iface;
319                 }
320         }
321
322         // Special case for intefaces that are being DHCP configured
323         // - If the interface address is 0.0.0.0, then if there is no match for the
324         //   destination the packet is treated as if it was addressed to 0.0.0.0
325         if( zero_iface && Broadcast )
326         {
327                 LOG("Using 0.0.0.0 interface with magic!");
328                 LEAVE('p', zero_iface);
329                 return zero_iface;
330         }
331
332         LEAVE('n');
333         return NULL;
334 }
335
336 /**
337  * \brief Convert a network prefix to a netmask
338  * \param FixedBits     Netmask size (/n)
339  * 
340  * For example /24 will become 255.255.255.0 (0xFFFFFF00)
341  */
342 Uint32 IPv4_Netmask(int FixedBits)
343 {
344         Uint32  ret = 0xFFFFFFFF;
345         if( FixedBits == 0 )
346                 return 0;
347         if( FixedBits < 32 )
348         {
349                 ret >>= (32-FixedBits);
350                 ret <<= (32-FixedBits);
351         }
352         // Returns a native endian netmask
353         return ret;
354 }
355
356 /**
357  * \brief Calculate the IPv4 Checksum
358  * \param Buf   Input buffer
359  * \param Size  Size of input
360  * 
361  * One's complement sum of all 16-bit words (bitwise inverted)
362  */
363 Uint16 IPv4_Checksum(const void *Buf, size_t Length)
364 {
365         //Debug_HexDump("IPv4_Checksum", Buf, Length);
366         const Uint16    *words = Buf;
367         Uint32  sum = 0;
368          int    i;
369         
370         // Sum all whole words
371         for(i = 0; i < Length/2; i++ )
372         {
373                 sum += ntohs(words[i]);
374         }
375         if( Length & 1 )
376                 sum += ntohs( words[i] & 0xFF );
377         
378         // Apply one's complement
379         while (sum >> 16)
380                 sum = (sum & 0xFFFF) + (sum >> 16);
381         
382         return ~sum;
383 }
384
385 /**
386  * \brief Sends an ICMP Echo and waits for a reply
387  * \param IFace Interface
388  * \param Addr  Destination address
389  */
390 int IPv4_Ping(tInterface *IFace, tIPv4 Addr)
391 {
392         return ICMP_Ping(IFace, Addr);
393 }

UCC git Repository :: git.ucc.asn.au