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

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