Modules/IPStack - Add structure for propagating ICMP errors
[tpg/acess2.git] / KernelLand / Modules / IPStack / ipv6.c
1 /*
2  * Acess2 IP Stack
3  * - IPv6 Protcol Handling
4  */
5 #include "ipstack.h"
6 #include "link.h"
7 #include "ipv6.h"
8 #include "firewall.h"
9 #include "hwaddr_cache.h"
10
11 // === IMPORTS ===
12 extern tInterface       *gIP_Interfaces;
13 extern Uint32   IPv4_Netmask(int FixedBits);
14
15 // === PROTOTYPES ===
16  int    IPv6_Initialise();
17 // int  IPv6_RegisterCallback(int ID, tIPCallback Callback);
18 void    IPv6_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer);
19 tInterface      *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast);
20
21 // === GLOBALS ===
22 tIPRxCallback*  gaIPv6_Callbacks[256];
23
24 // === CODE ===
25 /**
26  * \brief Initialise the IPv6 handling code
27  */
28 int IPv6_Initialise()
29 {
30         Link_RegisterType(IPV6_ETHERNET_ID, IPv6_int_GetPacket);
31         return 1;
32 }
33
34 /**
35  * \brief Registers a callback
36  * \param ID    8-bit packet type ID
37  * \param Callback      Callback function
38  */
39 int IPv6_RegisterCallback(int ID, tIPRxCallback* Callback)
40 {
41         if( ID < 0 || ID > 255 )        return 0;
42         if( gaIPv6_Callbacks[ID] )      return 0;
43         gaIPv6_Callbacks[ID] = Callback;
44         return 1;
45 }
46
47 /**
48  * \brief Creates and sends an IPv6 Packet
49  * \param Iface Interface
50  * \param Destination   Destination IP
51  * \param Protocol      Protocol ID
52  * \param Length        Data Length
53  * \param Data  Packet Data
54  * \return Boolean Success
55  */
56 int IPv6_SendPacket(tInterface *Iface, tIPv6 Destination, int Protocol, tIPStackBuffer *Buffer)
57 {
58         size_t length = IPStack_Buffer_GetLength(Buffer);
59         
60         // Resolve destination
61         tMacAddr to = HWCache_Resolve(Iface, &Destination);
62         if( MAC_EQU(to, cMAC_ZERO) ) {
63                 // No route to host
64                 return 0;
65         }
66         
67         // Build up header
68         tIPv6Header     hdr;
69         hdr.Version = 6;
70         hdr.TrafficClass = 0;
71         hdr.FlowLabel = 0;
72         hdr.Head = htonl(hdr.Head);
73         hdr.PayloadLength = htons(length);
74         hdr.NextHeader = Protocol;      // TODO: Routing header?
75         hdr.HopLimit = 64;      // TODO: Configurable TTL
76         hdr.Source = *(tIPv6*)Iface->Address;
77         hdr.Destination = Destination;
78         
79         IPStack_Buffer_AppendSubBuffer(Buffer, sizeof(hdr), 0, &hdr, NULL, NULL);
80         
81         Link_SendPacket(Iface->Adapter, IPV6_ETHERNET_ID, to, Buffer);
82
83         return 1;
84 }
85
86 /**
87  * \fn void IPv6_int_GetPacket(tInterface *Interface, tMacAddr From, int Length, void *Buffer)
88  * \brief Process an IPv6 Packet
89  * \param Interface     Input interface
90  * \param From  Source MAC address
91  * \param Length        Packet length
92  * \param Buffer        Packet data
93  */
94 void IPv6_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
95 {
96         tInterface      *iface;
97         tIPv6Header     *hdr = Buffer;
98          int    ret;
99         char    *dataPtr;
100         Uint8   nextHeader;
101         
102         if(Length < sizeof(tIPv6Header))        return;
103         
104         hdr->Head = ntohl(hdr->Head);
105         
106         //if( ((hdr->Head >> (20+8)) & 0xF) != 6 )
107         if( hdr->Version != 6 )
108                 return;
109         
110         #if 1
111         Log_Debug("IPv6", "hdr = {");
112         Log_Debug("IPv6", " .Version       = %i", hdr->Version );
113         Log_Debug("IPv6", " .TrafficClass  = %i", hdr->TrafficClass );
114         Log_Debug("IPv6", " .FlowLabel     = %i", hdr->FlowLabel );
115         Log_Debug("IPv6", " .PayloadLength = 0x%04x", ntohs(hdr->PayloadLength) );
116         Log_Debug("IPv6", " .NextHeader    = 0x%02x", hdr->NextHeader );
117         Log_Debug("IPv6", " .HopLimit      = 0x%02x", hdr->HopLimit );
118         Log_Debug("IPv6", " .Source        = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Source );
119         Log_Debug("IPv6", " .Destination   = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Destination );
120         Log_Debug("IPv6", "}");
121         #endif
122         
123         // No checksum in IPv6
124         
125         // Check Packet length
126         if( ntohs(hdr->PayloadLength)+sizeof(tIPv6Header) > Length) {
127                 Log_Log("IPv6", "hdr->PayloadLength(%i) > Length(%i)", ntohs(hdr->PayloadLength), Length);
128                 return;
129         }
130         
131         // Process Options
132         nextHeader = hdr->NextHeader;
133         dataPtr = hdr->Data;
134         for( ;; )
135         {
136                 struct {
137                         Uint8   NextHeader;
138                         Uint8   Length; // In 8-byte chunks, with 0 being 8 bytes long
139                         Uint8   Data[];
140                 }       *optionHdr;
141                 optionHdr = (void*)dataPtr;
142                 // Hop-by-hop options
143                 if(nextHeader == 0)
144                 {
145                         // TODO: Parse the options (actually, RFC2460 doesn't specify any)
146                         // Two Defined: Pad1 and PadN
147                 }
148                 // Routing Options
149                 else if(nextHeader == 43)
150                 {
151                         // TODO: Routing Header options
152                 }
153                 else
154                 {
155                         break;  // Unknown, pass to next layer
156                 }
157                 nextHeader = optionHdr->NextHeader;
158                 dataPtr += (optionHdr->Length + 1) * 8; // 8-octet length (0 = 8 bytes long)
159         }
160         
161         // Get Interface (allowing broadcasts)
162         iface = IPv6_GetInterface(Adapter, hdr->Destination, 1);
163         
164         // Firewall rules
165         if( iface ) {
166                 // Incoming Packets
167                 ret = IPTables_TestChain("INPUT",
168                         6, &hdr->Source, &hdr->Destination,
169                         hdr->NextHeader, 0,
170                         hdr->PayloadLength, hdr->Data
171                         );
172         }
173         else {
174                 // Routed packets
175                 
176                 // If routing is disabled globally, or if it's disabled for v6 only, drop
177                 if( false || false )
178                 {
179                         return ;
180                 }
181                 // TODO: Defer routing of packet
182                 LOG("Route the packet");
183                 // Drop the packet if the TTL is zero
184                 if( hdr->HopLimit == 0 ) {
185                         Log_Warning("IPv6", "TODO: Send ICMP-Timeout when TTL exceeded");
186                         return ;
187                 }
188                 hdr->HopLimit --;
189                 
190                 ret = IPTables_TestChain("FORWARD",
191                         6, &hdr->Source, &hdr->Destination,
192                         hdr->NextHeader, 0,
193                         hdr->PayloadLength, hdr->Data
194                         );
195         }
196         
197         switch(ret)
198         {
199         // 0 - Allow
200         case 0: break;
201         // 1 - Silent Drop
202         case 1:
203                 Log_Debug("IPv6", "Silently dropping packet");
204                 return ;
205         // Unknown, silent drop
206         default:
207                 return ;
208         }
209         
210         // Routing
211         if(!iface)
212         {
213                 // TODO: Use tIPStackBuffer instead, for refcounting
214                 //IPStack_RoutePacket(6, &hdr->Destination, Length, Buffer);
215                 return ;
216         }
217         
218         // Populate cache
219         // - TODO: Populate when routing using source address match for iface
220         if( IPStack_CompareAddress(6, &hdr->Source, iface->Address, iface->SubnetBits) )
221         {
222                 HWCache_Set(Adapter, 6, &hdr->Source, &From);
223         }
224         
225         // Send it on
226         if( !gaIPv6_Callbacks[hdr->NextHeader] ) {
227                 Log_Log("IPv6", "Unknown Protocol %i", hdr->NextHeader);
228                 return ;
229         }
230         
231         gaIPv6_Callbacks[hdr->NextHeader]( iface, &hdr->Source, hdr->PayloadLength, hdr->Data );
232 }
233
234 /**
235  * \fn tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address)
236  * \brief Searches an adapter for a matching address
237  * \param Adapter       Source adapter
238  * \param Address       Destination Address
239  * \param Broadcast     Allow broadcast?
240  */
241 tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast)
242 {
243          int    i, j;
244         tInterface      *iface = NULL;
245         Uint32  netmask;
246         
247         for( iface = gIP_Interfaces; iface; iface = iface->Next)
248         {
249                 tIPv6   *thisAddr;
250                 
251                 // Check for this adapter
252                 if( iface->Adapter != Adapter ) continue;
253                 
254                 // Skip non-IPv6 Interfaces
255                 if( iface->Type != 6 )  continue;
256                 
257                 thisAddr = (tIPv6*)iface->Address;
258                 // If the address is a perfect match, return this interface
259                 if( IP6_EQU(Address, *thisAddr) )       return iface;
260                 
261                 // Check if we want to match broadcast addresses
262                 if( !Broadcast )        continue;
263                 
264                 // Check for broadcast
265                 // - Check first DWORDs
266                 if( iface->SubnetBits > 32 && Address.L[0] != thisAddr->L[0] )
267                         continue;
268                 if( iface->SubnetBits > 64 && Address.L[1] != thisAddr->L[1] )
269                         continue;
270                 if( iface->SubnetBits > 96 && Address.L[2] != thisAddr->L[2] )
271                         continue;
272                 
273                 // Check final DWORD
274                 j = iface->SubnetBits / 32;
275                 i = iface->SubnetBits % 32;
276                 netmask = IPv4_Netmask( iface->SubnetBits % 32 );
277                 
278                 // Check the last bit of the netmask
279                 if( (Address.L[j] >> i) != (thisAddr->L[j] >> i) )      continue;
280                 
281                 // Check that the host portion is one
282                 if( (Address.L[j] & ~netmask) != (0xFFFFFFFF & ~netmask) )      continue;
283                 if( j >= 2 && Address.L[3] != 0xFFFFFFFF)       continue;
284                 if( j >= 1 && Address.L[2] != 0xFFFFFFFF)       continue;
285                 if( j >= 0 && Address.L[1] != 0xFFFFFFFF)       continue;
286                 
287                 return iface;
288         }
289         return NULL;
290 }

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