Kernel - Instruction tracing support
[tpg/acess2.git] / Modules / IPStack / udp.c
index fe13a3e..c1dbe92 100644 (file)
 // === PROTOTYPES ===
 void   UDP_Initialise();
 void   UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
+void   UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer);
+void   UDP_SendPacket(tUDPChannel *Channel, void *Data, size_t Length);
 // --- Listening Server
 tVFS_Node      *UDP_Server_Init(tInterface *Interface);
 char   *UDP_Server_ReadDir(tVFS_Node *Node, int ID);
-tVFS_Node      UDP_Server_FindDir(tVFS_Node *Node, char *Name);
+tVFS_Node      *UDP_Server_FindDir(tVFS_Node *Node, const char *Name);
  int   UDP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data);
 void   UDP_Server_Close(tVFS_Node *Node);
 // --- Client Channels
@@ -29,11 +31,16 @@ Uint16      UDP_int_AllocatePort();
 void   UDP_int_FreePort(Uint16 Port);
 
 // === GLOBALS ===
-tSpinlock      glUDP_Channels;
+tMutex glUDP_Servers;
+tUDPServer     *gpUDP_Servers;
+
+tMutex glUDP_Channels;
 tUDPChannel    *gpUDP_Channels;
-tSpinlock      glUDP_Ports;
+
+tMutex glUDP_Ports;
 Uint32 gUDP_Ports[0x10000/32];
-//tSocketFile  gUDP_ServerFile = {NULL, "udps", UDP_Server_Init};
+
+tSocketFile    gUDP_ServerFile = {NULL, "udps", UDP_Server_Init};
 tSocketFile    gUDP_ClientFile = {NULL, "udpc", UDP_Channel_Init};
 
 // === CODE ===
@@ -43,29 +50,24 @@ tSocketFile gUDP_ClientFile = {NULL, "udpc", UDP_Channel_Init};
  */
 void UDP_Initialise()
 {
+       IPStack_AddFile(&gUDP_ServerFile);
        IPStack_AddFile(&gUDP_ClientFile);
+       //IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket, UDP_Unreachable);
        IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket);
 }
 
 /**
- * \fn void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
- * \brief Handles a packet from the IP Layer
+ * \brief Scan a list of tUDPChannels and find process the first match
+ * \return 0 if no match was found, -1 on error and 1 if a match was found
  */
-void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
+int UDP_int_ScanList(tUDPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer)
 {
        tUDPHeader      *hdr = Buffer;
        tUDPChannel     *chan;
        tUDPPacket      *pack;
         int    len;
        
-       Log("[UDP  ] hdr->SourcePort = %i", ntohs(hdr->SourcePort));
-       Log("[UDP  ] hdr->DestPort = %i", ntohs(hdr->DestPort));
-       Log("[UDP  ] hdr->Length = %i", ntohs(hdr->Length));
-       Log("[UDP  ] hdr->Checksum = 0x%x", ntohs(hdr->Checksum));
-       
-       // Check registered connections
-       LOCK(&glUDP_Channels);
-       for(chan = gpUDP_Channels;
+       for(chan = List;
                chan;
                chan = chan->Next)
        {
@@ -74,17 +76,18 @@ void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe
                if(chan->RemotePort != ntohs(hdr->SourcePort))  continue;
                
                if(Interface->Type == 4) {
-                       if(IP4_EQU(chan->RemoteAddr.v4, *(tIPv4*)Address))      continue;
+                       if(!IP4_EQU(chan->RemoteAddr.v4, *(tIPv4*)Address))     continue;
                }
                else if(Interface->Type == 6) {
-                       if(IP6_EQU(chan->RemoteAddr.v6, *(tIPv6*)Address))      continue;
+                       if(!IP6_EQU(chan->RemoteAddr.v6, *(tIPv6*)Address))     continue;
                }
                else {
                        Warning("[UDP  ] Address type %i unknown", Interface->Type);
-                       RELEASE(&glUDP_Channels);
-                       return ;
+                       Mutex_Release(&glUDP_Channels);
+                       return -1;
                }
                
+               Log("[UDP  ] Recieved packet for %p", chan);
                // Create the cached packet
                len = ntohs(hdr->Length);
                pack = malloc(sizeof(tUDPPacket) + len);
@@ -93,19 +96,65 @@ void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffe
                memcpy(pack->Data, hdr->Data, len);
                
                // Add the packet to the channel's queue
-               LOCK(&chan->lQueue);
+               SHORTLOCK(&chan->lQueue);
                if(chan->Queue)
                        chan->QueueEnd->Next = pack;
                else
                        chan->QueueEnd = chan->Queue = pack;
-               RELEASE(&chan->lQueue);
-               RELEASE(&glUDP_Channels);
-               return ;
+               SHORTREL(&chan->lQueue);
+               Mutex_Release(&glUDP_Channels);
+               return 1;
        }
+       return 0;
+}
+
+/**
+ * \fn void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
+ * \brief Handles a packet from the IP Layer
+ */
+void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
+{
+       tUDPHeader      *hdr = Buffer;
+       tUDPServer      *srv;
+        int    ret;
+       
+       Log("[UDP  ] hdr->SourcePort = %i", ntohs(hdr->SourcePort));
+       Log("[UDP  ] hdr->DestPort = %i", ntohs(hdr->DestPort));
+       Log("[UDP  ] hdr->Length = %i", ntohs(hdr->Length));
+       Log("[UDP  ] hdr->Checksum = 0x%x", ntohs(hdr->Checksum));
+       
+       // Check registered connections
+       Mutex_Acquire(&glUDP_Channels);
+       ret = UDP_int_ScanList(gpUDP_Channels, Interface, Address, Length, Buffer);
+       Mutex_Release(&glUDP_Channels);
+       if(ret != 0)    return ;
+       
        
        // TODO: Server/Listener
+       Mutex_Acquire(&glUDP_Servers);
+       for(srv = gpUDP_Servers;
+               srv;
+               srv = srv->Next)
+       {
+               if(srv->Interface != Interface) continue;
+               if(srv->ListenPort != ntohs(hdr->DestPort))     continue;
+               ret = UDP_int_ScanList(srv->Channels, Interface, Address, Length, Buffer);
+               if(ret != 0)    break;
+               
+               // Add connection
+               Warning("[UDP  ] TODO - Add channel on connection");
+               //TODO
+       }
+       Mutex_Release(&glUDP_Servers);
+       
+}
+
+/**
+ * \brief Handle an ICMP Unrechable Error
+ */
+void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer)
+{
        
-       RELEASE(&glUDP_Channels);
 }
 
 /**
@@ -136,6 +185,184 @@ void UDP_SendPacket(tUDPChannel *Channel, void *Data, size_t Length)
        }
 }
 
+// --- Listening Server
+tVFS_Node *UDP_Server_Init(tInterface *Interface)
+{
+       tUDPServer      *new;
+       new = calloc( sizeof(tUDPServer), 1 );
+       if(!new)        return NULL;
+       
+       new->Node.ImplPtr = new;
+       new->Node.Flags = VFS_FFLAG_DIRECTORY;
+       new->Node.NumACLs = 1;
+       new->Node.ACLs = &gVFS_ACL_EveryoneRX;
+       new->Node.ReadDir = UDP_Server_ReadDir;
+       new->Node.FindDir = UDP_Server_FindDir;
+       new->Node.IOCtl = UDP_Server_IOCtl;
+       new->Node.Close = UDP_Server_Close;
+       
+       Mutex_Acquire(&glUDP_Servers);
+       new->Next = gpUDP_Servers;
+       gpUDP_Servers = new;
+       Mutex_Release(&glUDP_Servers);
+       
+       return &new->Node;
+}
+
+/**
+ * \brief Wait for a connection and return its ID in a string
+ */
+char *UDP_Server_ReadDir(tVFS_Node *Node, int ID)
+{
+       tUDPServer      *srv = Node->ImplPtr;
+       tUDPChannel     *chan;
+       char    *ret;
+       
+       if( srv->ListenPort == 0 )      return NULL;
+       
+       // Lock (so another thread can't collide with us here) and wait for a connection
+       Mutex_Acquire( &srv->Lock );
+       while( srv->NewChannels == NULL )       Threads_Yield();
+       // Pop the connection off the new list
+       chan = srv->NewChannels;
+       srv->NewChannels = chan->Next;
+       // Release the lock
+       Mutex_Release( &srv->Lock );
+       
+       // Create the ID string and return it
+       ret = malloc(11+1);
+       sprintf(ret, "%i", chan->Node.ImplInt);
+       
+       return ret;
+}
+
+/**
+ * \brief Take a string and find the channel
+ */
+tVFS_Node *UDP_Server_FindDir(tVFS_Node *Node, const char *Name)
+{
+       tUDPServer      *srv = Node->ImplPtr;
+       tUDPChannel     *chan;
+        int    id = atoi(Name);
+       
+       for(chan = srv->Channels;
+               chan;
+               chan = chan->Next)
+       {
+               if( chan->Node.ImplInt < id )   continue;
+               if( chan->Node.ImplInt > id )   break;  // Go sorted lists!
+               
+               return &chan->Node;
+       }
+       
+       return NULL;
+}
+
+/**
+ * \brief Names for server IOCtl Calls
+ */
+static const char *casIOCtls_Server[] = {
+       DRV_IOCTLNAMES,
+       "getset_listenport",
+       NULL
+       };
+/**
+ * \brief Channel IOCtls
+ */
+int UDP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data)
+{
+       tUDPServer      *srv = Node->ImplPtr;
+       
+       ENTER("pNode iID pData", Node, ID, Data);
+       switch(ID)
+       {
+       BASE_IOCTLS(DRV_TYPE_MISC, "UDP Server", 0x100, casIOCtls_Server);
+       
+       case 4: // getset_localport (returns bool success)
+               if(!Data)       LEAVE_RET('i', srv->ListenPort);
+               if(!CheckMem( Data, sizeof(Uint16) ) ) {
+                       LOG("Invalid pointer %p", Data);
+                       LEAVE_RET('i', -1);
+               }
+               // Set port
+               srv->ListenPort = *(Uint16*)Data;
+               // Permissions check (Ports lower than 1024 are root-only)
+               if(srv->ListenPort != 0 && srv->ListenPort < 1024) {
+                       if( Threads_GetUID() != 0 ) {
+                               LOG("Attempt by non-superuser to listen on port %i", srv->ListenPort);
+                               srv->ListenPort = 0;
+                               LEAVE_RET('i', -1);
+                       }
+               }
+               // Allocate a random port if requested
+               if( srv->ListenPort == 0 )
+                       srv->ListenPort = UDP_int_AllocatePort();
+               else
+               {
+                       // Else, mark the requested port as used
+                       if( UDP_int_MarkPortAsUsed(srv->ListenPort) == 0 ) {
+                               LOG("Port %i us currently in use", srv->ListenPort);
+                               srv->ListenPort = 0;
+                               LEAVE_RET('i', -1);
+                       }
+                       LEAVE_RET('i', 1);
+               }
+               LEAVE_RET('i', 1);
+       
+       default:
+               LEAVE_RET('i', -1);
+       }
+       LEAVE_RET('i', 0);
+}
+
+void UDP_Server_Close(tVFS_Node *Node)
+{
+       tUDPServer      *srv = Node->ImplPtr;
+       tUDPServer      *prev;
+       tUDPChannel     *chan;
+       tUDPPacket      *tmp;
+       
+       
+       // Remove from the main list first
+       Mutex_Acquire(&glUDP_Servers);
+       if(gpUDP_Servers == srv)
+               gpUDP_Servers = gpUDP_Servers->Next;
+       else
+       {
+               for(prev = gpUDP_Servers;
+                       prev->Next && prev->Next != srv;
+                       prev = prev->Next);
+               if(!prev->Next)
+                       Warning("[UDP  ] Bookeeping Fail, server %p is not in main list", srv);
+               else
+                       prev->Next = prev->Next->Next;
+       }
+       Mutex_Release(&glUDP_Servers);
+       
+       
+       Mutex_Acquire(&srv->Lock);
+       for(chan = srv->Channels;
+               chan;
+               chan = chan->Next)
+       {
+               // Clear Queue
+               SHORTLOCK(&chan->lQueue);
+               while(chan->Queue)
+               {
+                       tmp = chan->Queue;
+                       chan->Queue = tmp->Next;
+                       free(tmp);
+               }
+               SHORTREL(&chan->lQueue);
+               
+               // Free channel structure
+               free(chan);
+       }
+       Mutex_Release(&srv->Lock);
+       
+       free(srv);
+}
+
 // --- Client Channels
 tVFS_Node *UDP_Channel_Init(tInterface *Interface)
 {
@@ -150,10 +377,10 @@ tVFS_Node *UDP_Channel_Init(tInterface *Interface)
        new->Node.IOCtl = UDP_Channel_IOCtl;
        new->Node.Close = UDP_Channel_Close;
        
-       LOCK(&glUDP_Channels);
+       Mutex_Acquire(&glUDP_Channels);
        new->Next = gpUDP_Channels;
        gpUDP_Channels = new;
-       RELEASE(&glUDP_Channels);
+       Mutex_Release(&glUDP_Channels);
        
        return &new->Node;
 }
@@ -173,15 +400,19 @@ Uint64 UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buf
        
        for(;;)
        {
-               LOCK(&chan->lQueue);
+               VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "UDP_Channel_Read");
+               SHORTLOCK(&chan->lQueue);
                if(chan->Queue == NULL) {
-                       RELEASE(&chan->lQueue);
+                       SHORTREL(&chan->lQueue);
                        continue;
                }
                pack = chan->Queue;
                chan->Queue = pack->Next;
-               if(!chan->Queue)        chan->QueueEnd = NULL;
-               RELEASE(&chan->lQueue);
+               if(!chan->Queue) {
+                       chan->QueueEnd = NULL;
+                       VFS_MarkAvaliable(Node, 0);     // Nothing left
+               }
+               SHORTREL(&chan->lQueue);
                break;
        }
        
@@ -294,7 +525,7 @@ void UDP_Channel_Close(tVFS_Node *Node)
        tUDPChannel     *prev;
        
        // Remove from the main list first
-       LOCK(&glUDP_Channels);
+       Mutex_Acquire(&glUDP_Channels);
        if(gpUDP_Channels == chan)
                gpUDP_Channels = gpUDP_Channels->Next;
        else
@@ -307,10 +538,10 @@ void UDP_Channel_Close(tVFS_Node *Node)
                else
                        prev->Next = prev->Next->Next;
        }
-       RELEASE(&glUDP_Channels);
+       Mutex_Release(&glUDP_Channels);
        
        // Clear Queue
-       LOCK(&chan->lQueue);
+       SHORTLOCK(&chan->lQueue);
        while(chan->Queue)
        {
                tUDPPacket      *tmp;
@@ -318,7 +549,7 @@ void UDP_Channel_Close(tVFS_Node *Node)
                chan->Queue = tmp->Next;
                free(tmp);
        }
-       RELEASE(&chan->lQueue);
+       SHORTREL(&chan->lQueue);
        
        // Free channel structure
        free(chan);
@@ -330,7 +561,7 @@ void UDP_Channel_Close(tVFS_Node *Node)
 Uint16 UDP_int_AllocatePort()
 {
         int    i;
-       LOCK(&glUDP_Ports);
+       Mutex_Acquire(&glUDP_Ports);
        // Fast Search
        for( i = UDP_ALLOC_BASE; i < 0x10000; i += 32 )
                if( gUDP_Ports[i/32] != 0xFFFFFFFF )
@@ -341,7 +572,7 @@ Uint16 UDP_int_AllocatePort()
                if( !(gUDP_Ports[i/32] & (1 << (i%32))) )
                        return i;
        }
-       RELEASE(&glUDP_Ports);
+       Mutex_Release(&glUDP_Ports);
 }
 
 /**
@@ -350,13 +581,13 @@ Uint16 UDP_int_AllocatePort()
  */
 int UDP_int_MarkPortAsUsed(Uint16 Port)
 {
-       LOCK(&glUDP_Ports);
+       Mutex_Acquire(&glUDP_Ports);
        if( gUDP_Ports[Port/32] & (1 << (Port%32)) ) {
                return 0;
-               RELEASE(&glUDP_Ports);
+               Mutex_Release(&glUDP_Ports);
        }
        gUDP_Ports[Port/32] |= 1 << (Port%32);
-       RELEASE(&glUDP_Ports);
+       Mutex_Release(&glUDP_Ports);
        return 1;
 }
 
@@ -365,7 +596,7 @@ int UDP_int_MarkPortAsUsed(Uint16 Port)
  */
 void UDP_int_FreePort(Uint16 Port)
 {
-       LOCK(&glUDP_Ports);
+       Mutex_Acquire(&glUDP_Ports);
        gUDP_Ports[Port/32] &= ~(1 << (Port%32));
-       RELEASE(&glUDP_Ports);
+       Mutex_Release(&glUDP_Ports);
 }

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