Sorting source tree a bit
[tpg/acess2.git] / KernelLand / Modules / IPStack / ipv6.c
diff --git a/KernelLand/Modules/IPStack/ipv6.c b/KernelLand/Modules/IPStack/ipv6.c
new file mode 100644 (file)
index 0000000..7cb8e0a
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Acess2 IP Stack
+ * - IPv6 Protcol Handling
+ */
+#include "ipstack.h"
+#include "link.h"
+#include "ipv6.h"
+#include "firewall.h"
+
+// === IMPORTS ===
+extern tInterface      *gIP_Interfaces;
+extern Uint32  IPv4_Netmask(int FixedBits);
+
+// === PROTOTYPES ===
+ int   IPv6_Initialise();
+ int   IPv6_RegisterCallback(int ID, tIPCallback Callback);
+void   IPv6_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer);
+tInterface     *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast);
+
+// === GLOBALS ===
+tIPCallback    gaIPv6_Callbacks[256];
+
+// === CODE ===
+/**
+ * \brief Initialise the IPv6 handling code
+ */
+int IPv6_Initialise()
+{
+       Link_RegisterType(IPV6_ETHERNET_ID, IPv6_int_GetPacket);
+       return 1;
+}
+
+/**
+ * \brief Registers a callback
+ * \param ID   8-bit packet type ID
+ * \param Callback     Callback function
+ */
+int IPv6_RegisterCallback(int ID, tIPCallback Callback)
+{
+       if( ID < 0 || ID > 255 )        return 0;
+       if( gaIPv6_Callbacks[ID] )      return 0;
+       gaIPv6_Callbacks[ID] = Callback;
+       return 1;
+}
+
+/**
+ * \brief Creates and sends an IPv6 Packet
+ * \param Iface        Interface
+ * \param Destination  Destination IP
+ * \param Protocol     Protocol ID
+ * \param Length       Data Length
+ * \param Data Packet Data
+ * \return Boolean Success
+ */
+int IPv6_SendPacket(tInterface *Iface, tIPv6 Destination, int Protocol, size_t Length, const void *Data)
+{
+       return 0;
+}
+
+/**
+ * \fn void IPv6_int_GetPacket(tInterface *Interface, tMacAddr From, int Length, void *Buffer)
+ * \brief Process an IPv6 Packet
+ * \param Interface    Input interface
+ * \param From Source MAC address
+ * \param Length       Packet length
+ * \param Buffer       Packet data
+ */
+void IPv6_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer)
+{
+       tInterface      *iface;
+       tIPv6Header     *hdr = Buffer;
+        int    ret, dataLength;
+       char    *dataPtr;
+       Uint8   nextHeader;
+       
+       if(Length < sizeof(tIPv6Header))        return;
+       
+       hdr->Head = ntohl(hdr->Head);
+       
+       //if( ((hdr->Head >> (20+8)) & 0xF) != 6 )
+       if( hdr->Version != 6 )
+               return;
+       
+       #if 1
+       Log_Debug("IPv6", "hdr = {");
+       Log_Debug("IPv6", " .Version       = %i", hdr->Version );
+       Log_Debug("IPv6", " .TrafficClass  = %i", hdr->TrafficClass );
+       Log_Debug("IPv6", " .FlowLabel     = %i", hdr->FlowLabel );
+       Log_Debug("IPv6", " .PayloadLength = 0x%04x", ntohs(hdr->PayloadLength) );
+       Log_Debug("IPv6", " .NextHeader    = 0x%02x", hdr->NextHeader );
+       Log_Debug("IPv6", " .HopLimit      = 0x%02x", hdr->HopLimit );
+       Log_Debug("IPv6", " .Source        = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Source );
+       Log_Debug("IPv6", " .Destination   = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Destination );
+       Log_Debug("IPv6", "}");
+       #endif
+       
+       // No checksum in IPv6
+       
+       // Check Packet length
+       if( ntohs(hdr->PayloadLength)+sizeof(tIPv6Header) > Length) {
+               Log_Log("IPv6", "hdr->PayloadLength(%i) > Length(%i)", ntohs(hdr->PayloadLength), Length);
+               return;
+       }
+       
+       // Process Options
+       nextHeader = hdr->NextHeader;
+       dataPtr = hdr->Data;
+       dataLength = hdr->PayloadLength;
+       for( ;; )
+       {
+               struct {
+                       Uint8   NextHeader;
+                       Uint8   Length; // In 8-byte chunks, with 0 being 8 bytes long
+                       Uint8   Data[];
+               }       *optionHdr;
+               optionHdr = (void*)dataPtr;
+               // Hop-by-hop options
+               if(nextHeader == 0)
+               {
+                       // TODO: Parse the options (actually, RFC2460 doesn't specify any)
+               }
+               // Routing Options
+               else if(nextHeader == 43)
+               {
+                       // TODO: Routing Header options
+               }
+               else
+               {
+                       break;  // Unknown, pass on
+               }
+               nextHeader = optionHdr->NextHeader;
+               dataPtr += (optionHdr->Length + 1) * 8; // 8-octet length (0 = 8 bytes long)
+       }
+       
+       // Get Interface (allowing broadcasts)
+       iface = IPv6_GetInterface(Adapter, hdr->Destination, 1);
+       
+       // Firewall rules
+       if( iface ) {
+               // Incoming Packets
+               ret = IPTables_TestChain("INPUT",
+                       6, &hdr->Source, &hdr->Destination,
+                       hdr->NextHeader, 0,
+                       hdr->PayloadLength, hdr->Data
+                       );
+       }
+       else {
+               // Routed packets
+               ret = IPTables_TestChain("FORWARD",
+                       6, &hdr->Source, &hdr->Destination,
+                       hdr->NextHeader, 0,
+                       hdr->PayloadLength, hdr->Data
+                       );
+       }
+       
+       switch(ret)
+       {
+       // 0 - Allow
+       case 0: break;
+       // 1 - Silent Drop
+       case 1:
+               Log_Debug("IPv6", "Silently dropping packet");
+               return ;
+       // Unknown, silent drop
+       default:
+               return ;
+       }
+       
+       // Routing
+       if(!iface)
+       {
+               #if 0
+               tMacAddr        to;
+               tRoute  *rt;
+               
+               Log_Debug("IPv6", "Route the packet");
+               // Drop the packet if the TTL is zero
+               if( hdr->HopLimit == 0 ) {
+                       Log_Warning("IPv6", "TODO: Sent ICMP-Timeout when TTL exceeded");
+                       return ;
+               }
+               
+               hdr->HopLimit --;
+               
+               rt = IPStack_FindRoute(6, NULL, &hdr->Destination);     // Get the route (gets the interface)
+               to = ICMP6_ResolveHWAddr(rt->Interface, hdr->Destination);      // Resolve address
+               
+               // Send packet
+               Log_Log("IPv6", "Forwarding packet");
+               Link_SendPacket(rt->Interface->Adapter, IPV6_ETHERNET_ID, to, Length, Buffer);
+               #endif
+               
+               return ;
+       }
+       
+       // Send it on
+       if( !gaIPv6_Callbacks[hdr->NextHeader] ) {
+               Log_Log("IPv6", "Unknown Protocol %i", hdr->NextHeader);
+               return ;
+       }
+       
+       gaIPv6_Callbacks[hdr->NextHeader]( iface, &hdr->Source, hdr->PayloadLength, hdr->Data );
+}
+
+/**
+ * \fn tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address)
+ * \brief Searches an adapter for a matching address
+ * \param Adapter      Source adapter
+ * \param Address      Destination Address
+ * \param Broadcast    Allow broadcast?
+ */
+tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast)
+{
+        int    i, j;
+       tInterface      *iface = NULL;
+       Uint32  netmask;
+       
+       for( iface = gIP_Interfaces; iface; iface = iface->Next)
+       {
+               tIPv6   *thisAddr;
+               
+               // Check for this adapter
+               if( iface->Adapter != Adapter ) continue;
+               
+               // Skip non-IPv6 Interfaces
+               if( iface->Type != 6 )  continue;
+               
+               thisAddr = (tIPv6*)iface->Address;
+               // If the address is a perfect match, return this interface
+               if( IP6_EQU(Address, *thisAddr) )       return iface;
+               
+               // Check if we want to match broadcast addresses
+               if( !Broadcast )        continue;
+               
+               // Check for broadcast
+               // - Check first DWORDs
+               if( iface->SubnetBits > 32 && Address.L[0] != thisAddr->L[0] )
+                       continue;
+               if( iface->SubnetBits > 64 && Address.L[1] != thisAddr->L[1] )
+                       continue;
+               if( iface->SubnetBits > 96 && Address.L[2] != thisAddr->L[2] )
+                       continue;
+               
+               // Check final DWORD
+               j = iface->SubnetBits / 32;
+               i = iface->SubnetBits % 32;
+               netmask = IPv4_Netmask( iface->SubnetBits % 32 );
+               
+               // Check the last bit of the netmask
+               if( (Address.L[j] >> i) != (thisAddr->L[j] >> i) )      continue;
+               
+               // Check that the host portion is one
+               if( (Address.L[j] & ~netmask) != (0xFFFFFFFF & ~netmask) )      continue;
+               if( j >= 2 && Address.L[3] != 0xFFFFFFFF)       continue;
+               if( j >= 1 && Address.L[2] != 0xFFFFFFFF)       continue;
+               if( j >= 0 && Address.L[1] != 0xFFFFFFFF)       continue;
+               
+               return iface;
+       }
+       return NULL;
+}

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