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

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