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

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