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

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