IPStack - Removed client/server structure from UDP (now just one file)
[tpg/acess2.git] / Modules / IPStack / udp.c
1 /*
2  * Acess2 IP Stack
3  * - UDP Handling
4  */
5 #include "ipstack.h"
6 #include <tpl_drv_common.h>
7 #include "udp.h"
8
9 #define UDP_ALLOC_BASE  0xC000
10
11 // === PROTOTYPES ===
12 void    UDP_Initialise();
13 void    UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer);
14 void    UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer);
15 void    UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, void *Address, Uint16 Port, void *Data, size_t Length);
16 // --- Client Channels
17 tVFS_Node       *UDP_Channel_Init(tInterface *Interface);
18 Uint64  UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
19 Uint64  UDP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
20  int    UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data);
21 void    UDP_Channel_Close(tVFS_Node *Node);
22 // --- Helpers
23 Uint16  UDP_int_AllocatePort();
24  int    UDP_int_MarkPortAsUsed(Uint16 Port);
25 void    UDP_int_FreePort(Uint16 Port);
26
27 // === GLOBALS ===
28 tMutex  glUDP_Channels;
29 tUDPChannel     *gpUDP_Channels;
30
31 tMutex  glUDP_Ports;
32 Uint32  gUDP_Ports[0x10000/32];
33
34 tSocketFile     gUDP_SocketFile = {NULL, "udp", UDP_Channel_Init};
35
36 // === CODE ===
37 /**
38  * \fn void TCP_Initialise()
39  * \brief Initialise the TCP Layer
40  */
41 void UDP_Initialise()
42 {
43         IPStack_AddFile(&gUDP_SocketFile);
44         //IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket, UDP_Unreachable);
45         IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket);
46 }
47
48 /**
49  * \brief Scan a list of tUDPChannels and find process the first match
50  * \return 0 if no match was found, -1 on error and 1 if a match was found
51  */
52 int UDP_int_ScanList(tUDPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer)
53 {
54         tUDPHeader      *hdr = Buffer;
55         tUDPChannel     *chan;
56         tUDPPacket      *pack;
57          int    len;
58         
59         for(chan = List;
60                 chan;
61                 chan = chan->Next)
62         {
63                 // Match local endpoint
64                 if(chan->Interface && chan->Interface != Interface)     continue;
65                 if(chan->LocalPort != ntohs(hdr->DestPort))     continue;
66                 
67                 // Check for remote port restriction
68                 if(chan->Remote.Port && chan->Remote.Port != ntohs(hdr->SourcePort))
69                         continue;
70                 // Check for remote address restriction
71                 if(chan->RemoteMask)
72                 {
73                         if(chan->Remote.AddrType != Interface->Type)    continue;
74                         if(!IPStack_CompareAddress(Interface->Type, Address,
75                                 &chan->Remote.Addr, chan->RemoteMask)
76                                 )
77                                 continue;
78                 }
79                 
80                 Log_Log("UDP", "Recieved packet for %p", chan);
81                 // Create the cached packet
82                 len = ntohs(hdr->Length);
83                 pack = malloc(sizeof(tUDPPacket) + len);
84                 pack->Next = NULL;
85                 memcpy(&pack->Remote.Addr, Address, IPStack_GetAddressSize(Interface->Type));
86                 pack->Remote.Port = ntohs(hdr->SourcePort);
87                 pack->Length = len;
88                 memcpy(pack->Data, hdr->Data, len);
89                 
90                 // Add the packet to the channel's queue
91                 SHORTLOCK(&chan->lQueue);
92                 if(chan->Queue)
93                         chan->QueueEnd->Next = pack;
94                 else
95                         chan->QueueEnd = chan->Queue = pack;
96                 SHORTREL(&chan->lQueue);
97                 Mutex_Release(&glUDP_Channels);
98                 return 1;
99         }
100         return 0;
101 }
102
103 /**
104  * \fn void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
105  * \brief Handles a packet from the IP Layer
106  */
107 void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer)
108 {
109         tUDPHeader      *hdr = Buffer;
110         
111         Log_Debug("UDP", "hdr->SourcePort = %i", ntohs(hdr->SourcePort));
112         Log_Debug("UDP", "hdr->DestPort = %i", ntohs(hdr->DestPort));
113         Log_Debug("UDP", "hdr->Length = %i", ntohs(hdr->Length));
114         Log_Debug("UDP", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum));
115         
116         // Check registered connections
117         Mutex_Acquire(&glUDP_Channels);
118         UDP_int_ScanList(gpUDP_Channels, Interface, Address, Length, Buffer);
119         Mutex_Release(&glUDP_Channels);
120 }
121
122 /**
123  * \brief Handle an ICMP Unrechable Error
124  */
125 void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer)
126 {
127         
128 }
129
130 /**
131  * \brief Send a packet
132  * \param Channel       Channel to send the packet from
133  * \param Data  Packet data
134  * \param Length        Length in bytes of packet data
135  */
136 void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, void *Address, Uint16 Port, void *Data, size_t Length)
137 {
138         tUDPHeader      *hdr;
139
140         if(Channel->Interface && Channel->Interface->Type != AddrType)  return ;
141         
142         switch(AddrType)
143         {
144         case 4:
145                 // Create the packet
146                 hdr = malloc(sizeof(tUDPHeader)+Length);
147                 hdr->SourcePort = htons( Channel->LocalPort );
148                 hdr->DestPort = htons( Port );
149                 hdr->Length = htons( sizeof(tUDPHeader) + Length );
150                 hdr->Checksum = 0;      // Checksum can be zero on IPv4
151                 memcpy(hdr->Data, Data, Length);
152                 // Pass on the the IPv4 Layer
153                 // TODO: What if Channel->Interface is NULL here?
154                 IPv4_SendPacket(Channel->Interface, *(tIPv4*)Address, IP4PROT_UDP, 0, sizeof(tUDPHeader)+Length, hdr);
155                 // Free allocated packet
156                 free(hdr);
157                 break;
158         }
159 }
160
161 // --- Client Channels
162 tVFS_Node *UDP_Channel_Init(tInterface *Interface)
163 {
164         tUDPChannel     *new;
165         new = calloc( sizeof(tUDPChannel), 1 );
166         new->Interface = Interface;
167         new->Node.ImplPtr = new;
168         new->Node.NumACLs = 1;
169         new->Node.ACLs = &gVFS_ACL_EveryoneRW;
170         new->Node.Read = UDP_Channel_Read;
171         new->Node.Write = UDP_Channel_Write;
172         new->Node.IOCtl = UDP_Channel_IOCtl;
173         new->Node.Close = UDP_Channel_Close;
174         
175         Mutex_Acquire(&glUDP_Channels);
176         new->Next = gpUDP_Channels;
177         gpUDP_Channels = new;
178         Mutex_Release(&glUDP_Channels);
179         
180         return &new->Node;
181 }
182
183 /**
184  * \brief Read from the channel file (wait for a packet)
185  */
186 Uint64 UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
187 {
188         tUDPChannel     *chan = Node->ImplPtr;
189         tUDPPacket      *pack;
190         tUDPEndpoint    *ep;
191          int    ofs;
192         
193         if(chan->LocalPort == 0)        return 0;
194         
195         while(chan->Queue == NULL)      Threads_Yield();
196         
197         for(;;)
198         {
199                 VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "UDP_Channel_Read");
200                 SHORTLOCK(&chan->lQueue);
201                 if(chan->Queue == NULL) {
202                         SHORTREL(&chan->lQueue);
203                         continue;
204                 }
205                 pack = chan->Queue;
206                 chan->Queue = pack->Next;
207                 if(!chan->Queue) {
208                         chan->QueueEnd = NULL;
209                         VFS_MarkAvaliable(Node, 0);     // Nothing left
210                 }
211                 SHORTREL(&chan->lQueue);
212                 break;
213         }
214
215         // Check that the header fits
216         ep = Buffer;
217         ofs = 4 + IPStack_GetAddressSize(pack->Remote.AddrType);
218         if(Length < ofs) {
219                 free(pack);
220                 return 0;
221         }
222         
223         // Fill header
224         ep->Port = pack->Remote.Port;
225         ep->AddrType = pack->Remote.AddrType;
226         memcpy(&ep->Addr, &pack->Remote.Addr, IPStack_GetAddressSize(pack->Remote.AddrType));
227         
228         // Copy packet data
229         if(Length > ofs + pack->Length) Length = ofs + pack->Length;
230         memcpy((char*)Buffer + ofs, pack->Data, Length - ofs);
231
232         // Free cached packet
233         free(pack);
234         
235         return Length;
236 }
237
238 /**
239  * \brief Write to the channel file (send a packet)
240  */
241 Uint64 UDP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
242 {
243         tUDPChannel     *chan = Node->ImplPtr;
244         tUDPEndpoint    *ep;
245         void    *data;
246          int    ofs;
247         if(chan->LocalPort == 0)        return 0;
248         
249         ep = Buffer;    
250         ofs = 2 + 2 + IPStack_GetAddressSize( ep->AddrType );
251
252         data = (char*)Buffer + ofs;
253
254         UDP_SendPacketTo(chan, ep->AddrType, &ep->Addr, ep->Port, Buffer, (size_t)Length - ofs);
255         
256         return 0;
257 }
258
259 /**
260  * \brief Names for channel IOCtl Calls
261  */
262 static const char *casIOCtls_Channel[] = {
263         DRV_IOCTLNAMES,
264         "getset_localport",
265         "getset_remoteport",
266         "set_remotemask",
267         "set_remoteaddr",
268         NULL
269         };
270 /**
271  * \brief Channel IOCtls
272  */
273 int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data)
274 {
275         tUDPChannel     *chan = Node->ImplPtr;
276         ENTER("pNode iID pData", Node, ID, Data);
277         switch(ID)
278         {
279         BASE_IOCTLS(DRV_TYPE_MISC, "UDP Channel", 0x100, casIOCtls_Channel);
280         
281         case 4: // getset_localport (returns bool success)
282                 if(!Data)       LEAVE_RET('i', chan->LocalPort);
283                 if(!CheckMem( Data, sizeof(Uint16) ) ) {
284                         LOG("Invalid pointer %p", Data);
285                         LEAVE_RET('i', -1);
286                 }
287                 // Set port
288                 chan->LocalPort = *(Uint16*)Data;
289                 // Permissions check (Ports lower than 1024 are root-only)
290                 if(chan->LocalPort != 0 && chan->LocalPort < 1024) {
291                         if( Threads_GetUID() != 0 ) {
292                                 LOG("Attempt by non-superuser to listen on port %i", chan->LocalPort);
293                                 chan->LocalPort = 0;
294                                 LEAVE_RET('i', -1);
295                         }
296                 }
297                 // Allocate a random port if requested
298                 if( chan->LocalPort == 0 )
299                         chan->LocalPort = UDP_int_AllocatePort();
300                 else
301                 {
302                         // Else, mark the requested port as used
303                         if( UDP_int_MarkPortAsUsed(chan->LocalPort) == 0 ) {
304                                 LOG("Port %i us currently in use", chan->LocalPort);
305                                 chan->LocalPort = 0;
306                                 LEAVE_RET('i', 0);
307                         }
308                         LEAVE_RET('i', 1);
309                 }
310                 LEAVE_RET('i', 1);
311         
312         case 5: // getset_remoteport (returns bool success)
313                 if(!Data)       LEAVE_RET('i', chan->Remote.Port);
314                 if(!CheckMem( Data, sizeof(Uint16) ) ) {
315                         LOG("Invalid pointer %p", Data);
316                         LEAVE_RET('i', -1);
317                 }
318                 chan->Remote.Port = *(Uint16*)Data;
319                 return 1;
320         
321         case 6: // getset_remotemask (returns bool success)
322                 if(!Data)       LEAVE_RET('i', chan->RemoteMask);
323                 if(!CheckMem(Data, sizeof(int)))        LEAVE_RET('i', -1);
324                 if( !chan->Interface ) {
325                         LOG("Can't set remote mask on NULL interface");
326                         LEAVE_RET('i', -1);
327                 }
328                 if( *(int*)Data > IPStack_GetAddressSize(chan->Interface->Type) )
329                         LEAVE_RET('i', -1);
330                 chan->RemoteMask = *(int*)Data;
331                 return 1;       
332
333         case 7: // set_remoteaddr (returns bool success)
334                 if( !chan->Interface ) {
335                         LOG("Can't set remote address on NULL interface");
336                         LEAVE_RET('i', -1);
337                 }
338                 if(!CheckMem(Data, IPStack_GetAddressSize(chan->Interface->Type))) {
339                         LOG("Invalid pointer");
340                         LEAVE_RET('i', -1);
341                 }
342                 memcpy(&chan->Remote.Addr, Data, IPStack_GetAddressSize(chan->Interface->Type));
343                 return 0;
344         }
345         LEAVE_RET('i', 0);
346 }
347
348 /**
349  * \brief Close and destroy an open channel
350  */
351 void UDP_Channel_Close(tVFS_Node *Node)
352 {
353         tUDPChannel     *chan = Node->ImplPtr;
354         tUDPChannel     *prev;
355         
356         // Remove from the main list first
357         Mutex_Acquire(&glUDP_Channels);
358         if(gpUDP_Channels == chan)
359                 gpUDP_Channels = gpUDP_Channels->Next;
360         else
361         {
362                 for(prev = gpUDP_Channels;
363                         prev->Next && prev->Next != chan;
364                         prev = prev->Next);
365                 if(!prev->Next)
366                         Log_Warning("UDP", "Bookeeping Fail, channel %p is not in main list", chan);
367                 else
368                         prev->Next = prev->Next->Next;
369         }
370         Mutex_Release(&glUDP_Channels);
371         
372         // Clear Queue
373         SHORTLOCK(&chan->lQueue);
374         while(chan->Queue)
375         {
376                 tUDPPacket      *tmp;
377                 tmp = chan->Queue;
378                 chan->Queue = tmp->Next;
379                 free(tmp);
380         }
381         SHORTREL(&chan->lQueue);
382         
383         // Free channel structure
384         free(chan);
385 }
386
387 /**
388  * \return Port Number on success, or zero on failure
389  */
390 Uint16 UDP_int_AllocatePort()
391 {
392          int    i;
393         Mutex_Acquire(&glUDP_Ports);
394         // Fast Search
395         for( i = UDP_ALLOC_BASE; i < 0x10000; i += 32 )
396                 if( gUDP_Ports[i/32] != 0xFFFFFFFF )
397                         break;
398         if(i == 0x10000)        return 0;
399         for( ;; i++ )
400         {
401                 if( !(gUDP_Ports[i/32] & (1 << (i%32))) )
402                         return i;
403         }
404         Mutex_Release(&glUDP_Ports);
405 }
406
407 /**
408  * \brief Allocate a specific port
409  * \return Boolean Success
410  */
411 int UDP_int_MarkPortAsUsed(Uint16 Port)
412 {
413         Mutex_Acquire(&glUDP_Ports);
414         if( gUDP_Ports[Port/32] & (1 << (Port%32)) ) {
415                 return 0;
416                 Mutex_Release(&glUDP_Ports);
417         }
418         gUDP_Ports[Port/32] |= 1 << (Port%32);
419         Mutex_Release(&glUDP_Ports);
420         return 1;
421 }
422
423 /**
424  * \brief Free an allocated port
425  */
426 void UDP_int_FreePort(Uint16 Port)
427 {
428         Mutex_Acquire(&glUDP_Ports);
429         gUDP_Ports[Port/32] &= ~(1 << (Port%32));
430         Mutex_Release(&glUDP_Ports);
431 }

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