IPStack - Reworking of network device API
[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         
185         Log_Debug("IPv4", " From %i.%i.%i.%i to %i.%i.%i.%i",
186                 hdr->Source.B[0], hdr->Source.B[1], hdr->Source.B[2], hdr->Source.B[3],
187                 hdr->Destination.B[0], hdr->Destination.B[1], hdr->Destination.B[2], hdr->Destination.B[3]
188                 );
189
190         // TODO: Tell ARP?
191         ARP_UpdateCache4(hdr->Source, From);
192         
193         // Get Data and Data Length
194         dataLength = ntohs(hdr->TotalLength) - sizeof(tIPv4Header);
195         data = &hdr->Options[0];
196         
197         // Get Interface (allowing broadcasts)
198         iface = IPv4_GetInterface(Adapter, hdr->Destination, 1);
199         
200         // Firewall rules
201         if( iface ) {
202                 // Incoming Packets
203                 ret = IPTables_TestChain("INPUT",
204                         4, &hdr->Source, &hdr->Destination,
205                         hdr->Protocol, 0,
206                         dataLength, data
207                         );
208         }
209         else {
210                 // Routed packets
211                 ret = IPTables_TestChain("FORWARD",
212                         4, &hdr->Source, &hdr->Destination,
213                         hdr->Protocol, 0,
214                         dataLength, data
215                         );
216         }
217         switch(ret)
218         {
219         // 0 - Allow
220         case 0: break;
221         // 1 - Silent Drop
222         case 1:
223                 Log_Debug("IPv4", "Silently dropping packet");
224                 return ;
225         case -1:
226                 // Bad rule
227                 break ;
228         // Unknown, silent drop
229         default:
230                 Log_Warning("IPv4", "Unknown firewall rule");
231                 return ;
232         }
233         
234         // Routing
235         if(!iface)
236         {
237                 tMacAddr        to;
238                 tRoute  *rt;
239                 
240                 Log_Debug("IPv4", "Route the packet");
241                 // Drop the packet if the TTL is zero
242                 if( hdr->TTL == 0 ) {
243                         Log_Warning("IPv4", "TODO: Send ICMP-Timeout when TTL exceeded");
244                         return ;
245                 }
246                 
247                 hdr->TTL --;
248                 
249                 rt = IPStack_FindRoute(4, NULL, &hdr->Destination);     // Get the route (gets the interface)
250                 if( !rt || !rt->Interface )
251                         return ;
252                 to = ARP_Resolve4(rt->Interface, hdr->Destination);     // Resolve address
253                 if( MAC_EQU(to, cMAC_ZERO) )
254                         return ;
255                 
256                 // Send packet
257                 Log_Log("IPv4", "Forwarding packet to %i.%i.%i.%i (via %i.%i.%i.%i)",
258                         hdr->Destination.B[0], hdr->Destination.B[1],
259                         hdr->Destination.B[2], hdr->Destination.B[3],
260                         ((tIPv4*)rt->NextHop)->B[0], ((tIPv4*)rt->NextHop)->B[1],
261                         ((tIPv4*)rt->NextHop)->B[2], ((tIPv4*)rt->NextHop)->B[3]);
262                 Log_Warning("IPv4", "TODO: Implement forwarding with tIPStackBuffer");
263 //              Link_SendPacket(rt->Interface->Adapter, IPV4_ETHERNET_ID, to, Length, Buffer);
264                 
265                 return ;
266         }
267         
268         // Send it on
269         if( !gaIPv4_Callbacks[hdr->Protocol] ) {
270                 Log_Log("IPv4", "Unknown Protocol %i", hdr->Protocol);
271                 return ;
272         }
273         
274         gaIPv4_Callbacks[hdr->Protocol]( iface, &hdr->Source, dataLength, data );
275 }
276
277 /**
278  * \fn tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address)
279  * \brief Searches an adapter for a matching address
280  * \param Adapter       Incoming Adapter
281  * \param Address       Destination Address
282  * \param Broadcast     Allow broadcast packets
283  */
284 tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast)
285 {
286         tInterface      *iface = NULL;
287         Uint32  netmask;
288         Uint32  addr, this;
289
290         ENTER("pAdapter xAddress bBroadcast", Adapter, Address, Broadcast);     
291
292         addr = ntohl( Address.L );
293         LOG("addr = 0x%x", addr);
294         
295         for( iface = gIP_Interfaces; iface; iface = iface->Next)
296         {
297                 if( iface->Adapter != Adapter ) continue;
298                 if( iface->Type != 4 )  continue;
299                 if( IP4_EQU(Address, *(tIPv4*)iface->Address) ) {
300                         LOG("Exact match");
301                         LEAVE('p', iface);
302                         return iface;
303                 }
304                 
305                 if( !Broadcast )        continue;
306                 
307                 // Check for broadcast
308                 this = ntohl( ((tIPv4*)iface->Address)->L );
309                 netmask = IPv4_Netmask(iface->SubnetBits);
310                 LOG("iface addr = 0x%x, netmask = 0x%x (bits = %i)", this, netmask, iface->SubnetBits);
311
312                 if( (addr & netmask) == (this & netmask) && (addr & ~netmask) == (0xFFFFFFFF & ~netmask) )
313                 {
314                         LOG("Broadcast match");
315                         LEAVE('p', iface);
316                         return iface;
317                 }
318         }
319         LEAVE('n');
320         return NULL;
321 }
322
323 /**
324  * \brief Convert a network prefix to a netmask
325  * \param FixedBits     Netmask size (/n)
326  * 
327  * For example /24 will become 255.255.255.0 (0xFFFFFF00)
328  */
329 Uint32 IPv4_Netmask(int FixedBits)
330 {
331         Uint32  ret = 0xFFFFFFFF;
332         if( FixedBits == 0 )
333                 return 0;
334         if( FixedBits < 32 )
335         {
336                 ret >>= (32-FixedBits);
337                 ret <<= (32-FixedBits);
338         }
339         // Returns a native endian netmask
340         return ret;
341 }
342
343 /**
344  * \brief Calculate the IPv4 Checksum
345  * \param Buf   Input buffer
346  * \param Size  Size of input
347  * 
348  * One's complement sum of all 16-bit words (bitwise inverted)
349  */
350 Uint16 IPv4_Checksum(const void *Buf, size_t Length)
351 {
352         const Uint16    *words = Buf;
353         Uint32  sum = 0;
354          int    i;
355         
356         // Sum all whole words
357         for(i = 0; i < Length/2; i++ )
358         {
359                 sum += ntohs(words[i]);
360         }
361         if( Length & 1 )
362                 sum += ntohs( words[i] & 0xFF );
363         
364         // Apply one's complement
365         while (sum >> 16)
366                 sum = (sum & 0xFFFF) + (sum >> 16);
367         
368         return ~sum;
369 }
370
371 /**
372  * \brief Sends an ICMP Echo and waits for a reply
373  * \param IFace Interface
374  * \param Addr  Destination address
375  */
376 int IPv4_Ping(tInterface *IFace, tIPv4 Addr)
377 {
378         return ICMP_Ping(IFace, Addr);
379 }

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