Changes and Features to IPStack
[tpg/acess2.git] / Modules / IPStack / main.c
1 /*
2  * Acess2 IP Stack
3  * - Stack Initialisation
4  */
5 #define DEBUG   0
6 #define VERSION ((0<<8)|10)
7 #include "ipstack.h"
8 #include "link.h"
9 #include <modules.h>
10 #include <fs_devfs.h>
11 #include <tpl_drv_common.h>
12 #include <tpl_drv_network.h>
13
14 // === CONSTANTS ===
15 //! Default timeout value, 30 seconds
16 #define DEFAULT_TIMEOUT (30*1000)
17
18 // === IMPORTS ===
19 extern int      ARP_Initialise();
20 extern int      IPv4_Initialise();
21 extern int      IPv4_Ping(tInterface *Iface, tIPv4 Addr);
22
23 // === PROTOTYPES ===
24  int    IPStack_Install(char **Arguments);
25  int    IPStack_IOCtlRoot(tVFS_Node *Node, int ID, void *Data);
26 char    *IPStack_ReadDir(tVFS_Node *Node, int Pos);
27 tVFS_Node       *IPStack_FindDir(tVFS_Node *Node, char *Name);
28  int    IPStack_IOCtl(tVFS_Node *Node, int ID, void *Data);
29  int    IPStack_AddInterface(char *Device);
30 tAdapter        *IPStack_GetAdapter(char *Path);
31
32 // === GLOBALS ===
33 MODULE_DEFINE(0, VERSION, IPStack, IPStack_Install, NULL, NULL);
34 tDevFS_Driver   gIP_DriverInfo = {
35         NULL, "ip",
36         {
37         .Size = 0,      // Number of interfaces
38         .NumACLs = 1,
39         .ACLs = &gVFS_ACL_EveryoneRX,
40         .Flags = VFS_FFLAG_DIRECTORY,
41         .ReadDir = IPStack_ReadDir,
42         .FindDir = IPStack_FindDir,
43         .IOCtl = IPStack_IOCtlRoot
44         }
45 };
46 tSpinlock       glIP_Interfaces = 0;
47 tInterface      *gIP_Interfaces = NULL;
48 tInterface      *gIP_Interfaces_Last = NULL;
49  int    giIP_NextIfaceId = 1;
50 tSpinlock       glIP_Adapters = 0;
51 tAdapter        *gIP_Adapters = NULL;
52
53 // === CODE ===
54 /**
55  * \fn int IPStack_Install(char **Arguments)
56  * \brief Intialise the relevant parts of the stack and register with DevFS
57  */
58 int IPStack_Install(char **Arguments)
59 {
60          int    i = 0;
61         
62         // Install Handlers
63         ARP_Initialise();
64         IPv4_Initialise();
65         
66         if(Arguments)
67         {
68                 // Parse module arguments
69                 for( i = 0; Arguments[i]; i++ )
70                 {
71                         //if(strcmp(Arguments[i], "Device") == '=') {
72                         //      
73                         //}
74                 }
75         }
76         
77         DevFS_AddDevice( &gIP_DriverInfo );
78         
79         return 1;
80 }
81
82 /**
83  * \brief Read from the IP Stack's Device Directory
84  */
85 char *IPStack_ReadDir(tVFS_Node *Node, int Pos)
86 {
87         tInterface      *iface;
88         char    *name;
89         ENTER("pNode iPos", Node, Pos);
90         
91         // Traverse the list
92         for( iface = gIP_Interfaces; iface && Pos--; iface = iface->Next ) ;
93         
94         // Did we run off the end?
95         if(!iface) {
96                 LEAVE('n');
97                 return NULL;
98         }
99         
100         name = malloc(6);
101         name[0] = 'i';
102         name[1] = 'p';
103         
104         // Create the name
105         Pos = iface->Node.ImplInt;
106         if(Pos < 10) {
107                 name[2] = '0' + Pos;
108                 name[3] = '\0';
109         }
110         else if(Pos < 100) {
111                 name[2] = '0' + Pos/10;
112                 name[3] = '0' + Pos%10;
113                 name[4] = '\0';
114         }
115         else {
116                 name[2] = '0' + Pos/100;
117                 name[3] = '0' + (Pos/10)%10;
118                 name[4] = '0' + Pos%10;
119                 name[5] = '\0';
120         }
121         
122         LEAVE('s', name);
123         // Return the pre-generated name
124         return name;
125 }
126
127 /**
128  * \brief Get the node of an interface
129  */
130 tVFS_Node *IPStack_FindDir(tVFS_Node *Node, char *Name)
131 {
132          int    i, num;
133         tInterface      *iface;
134         
135         ENTER("pNode sName", Node, Name);
136         
137         if(Name[0] != 'i' || Name[1] != 'p') {
138                 LEAVE('n');
139                 return NULL;
140         }
141         
142         i = 2;  num = 0;
143         while('0' <= Name[i] && Name[i] <= '9')
144         {
145                 num *= 10;
146                 num += Name[i] - '0';
147                 i ++;
148         }
149         if(Name[i] != '\0') {
150                 LEAVE('n');
151                 return NULL;
152         }
153         
154         for( iface = gIP_Interfaces; iface; iface = iface->Next )
155         {
156                 if( iface->Node.ImplInt == num )
157                 {
158                         LEAVE('p', &iface->Node);
159                         return &iface->Node;
160                 }
161         }
162         LEAVE('p', NULL);
163         return NULL;
164 }
165
166 static const char *casIOCtls_Root[] = { DRV_IOCTLNAMES, "add_interface", NULL };
167 /**
168  * \brief Handles IOCtls for the IPStack root
169  */
170 int IPStack_IOCtlRoot(tVFS_Node *Node, int ID, void *Data)
171 {
172          int    tmp;
173         ENTER("pNode iID pData", Node, ID, Data);
174         
175         switch(ID)
176         {
177         // --- Standard IOCtls (0-3) ---
178         case DRV_IOCTL_TYPE:
179                 LEAVE('i', DRV_TYPE_MISC);
180                 return DRV_TYPE_MISC;
181         
182         case DRV_IOCTL_IDENT:
183                 if( !CheckMem( Data, 4 ) )      LEAVE_RET('i', -1);
184                 memcpy(Data, "IP\0\0", 4);
185                 LEAVE('i', 1);
186                 return 1;
187         
188         case DRV_IOCTL_VERSION:
189                 LEAVE('x', VERSION);
190                 return VERSION;
191         
192         case DRV_IOCTL_LOOKUP:
193                 if( !CheckString( Data ) )      LEAVE_RET('i', -1);
194                 LOG("Lookup '%s'", Data);
195                 if( Node == &gIP_DriverInfo.RootNode )
196                         tmp = LookupString( (char**)casIOCtls_Root, (char*)Data );
197                 else
198                         tmp = LookupString( (char**)casIOCtls_Iface, (char*)Data );
199                 LEAVE('i', tmp);
200                 return tmp;
201                 
202                 /*
203                  * add_interface
204                  * - Adds a new IP interface and binds it to a device
205                  */
206         case 4:
207                 if( Threads_GetUID() != 0 )     LEAVE_RET('i', -1);
208                 if( !CheckString( Data ) )      LEAVE_RET('i', -1);
209                 tmp = IPStack_AddInterface(Data);
210                 LEAVE_RET('i', tmp);
211         }
212         LEAVE('i', 0);
213         return 0;
214 }
215
216 static const char *casIOCtls_Iface[] = {
217         DRV_IOCTLNAMES,
218         "getset_type",
219         "get_address", "set_address",
220         "getset_subnet",
221         "get_gateway", "set_gateway",
222         "ping",
223         NULL
224         };
225 /**
226  * \brief Handles IOCtls for the IPStack interfaces
227  */
228 int IPStack_IOCtl(tVFS_Node *Node, int ID, void *Data)
229 {
230          int    tmp;
231         tInterface      *iface = (tInterface*)Node->ImplPtr;
232         ENTER("pNode iID pData", Node, ID, Data);
233         
234         switch(ID)
235         {
236         // --- Standard IOCtls (0-3) ---
237         case DRV_IOCTL_TYPE:
238                 LEAVE('i', DRV_TYPE_MISC);
239                 return DRV_TYPE_MISC;
240         
241         case DRV_IOCTL_IDENT:
242                 if( !CheckMem( Data, 4 ) )      LEAVE_RET('i', -1);
243                 memcpy(Data, "IP\0\0", 4);
244                 LEAVE('i', 1);
245                 return 1;
246         
247         case DRV_IOCTL_VERSION:
248                 LEAVE('x', VERSION);
249                 return VERSION;
250         
251         case DRV_IOCTL_LOOKUP:
252                 if( !CheckString( Data ) )      LEAVE_RET('i', -1);
253                 LOG("Lookup '%s'", Data);
254                 if( Node == &gIP_DriverInfo.RootNode )
255                         tmp = LookupString( (char**)casIOCtls_Root, (char*)Data );
256                 else
257                         tmp = LookupString( (char**)casIOCtls_Iface, (char*)Data );
258                 LEAVE('i', tmp);
259                 return tmp;
260         
261         /*
262          * getset_type
263          * - Get/Set the interface type
264          */
265         case 4:
266                 // Set Type?
267                 if( Data )
268                 {
269                         // Ok, it's set type
270                         if( Threads_GetUID() != 0 ) {
271                                 LOG("Attempt by non-root to alter an interface (%i)", Threads_GetUID());
272                                 LEAVE('i', -1);
273                                 return -1;
274                         }
275                         if( !CheckMem( Data, sizeof(int) ) ) {
276                                 LOG("Invalid pointer %p", Data);
277                                 LEAVE('i', -1);
278                                 return -1;
279                         }
280                         switch( *(int*)Data )
281                         {
282                         case 0: // Disable
283                                 iface->Type = 0;
284                                 memset(&iface->IP6, 0, sizeof(tIPv6));  // Clear address
285                                 break;
286                         case 4: // IPv4
287                                 iface->Type = 4;
288                                 memset(&iface->IP4, 0, sizeof(tIPv4));
289                                 break;
290                         case 6: // IPv6
291                                 iface->Type = 6;
292                                 memset(&iface->IP6, 0, sizeof(tIPv6));
293                                 break;
294                         default:
295                                 LEAVE('i', -1);
296                                 return -1;
297                         }
298                 }
299                 LEAVE('i', iface->Type);
300                 return iface->Type;
301         
302         /*
303          * get_address
304          * - Get the interface's address
305          */
306         case 5:
307                 switch(iface->Type)
308                 {
309                 case 0: LEAVE_RET('i', 1);
310                 case 4:
311                         if( !CheckMem( Data, sizeof(tIPv4) ) )  LEAVE_RET('i', -1);
312                         memcpy( Data, &iface->IP4.Address, sizeof(tIPv4) );
313                         LEAVE_RET('i', 1);
314                 case 6:
315                         if( !CheckMem( Data, sizeof(tIPv6) ) )  LEAVE_RET('i', -1);
316                         memcpy( Data, &iface->IP6.Address, sizeof(tIPv6) );
317                         LEAVE_RET('i', 1);
318                 }
319                 LEAVE_RET('i', 0);
320         
321         /*
322          * set_address
323          * - Get the interface's address
324          */
325         case 6:
326                 if( Threads_GetUID() != 0 )     LEAVE_RET('i', -1);
327                 switch(iface->Type)
328                 {
329                 case 0: LEAVE_RET('i', 1);
330                 case 4:
331                         if( !CheckMem( Data, sizeof(tIPv4) ) )  LEAVE_RET('i', -1);
332                         iface->Type = 0;        // One very hacky mutex/trash protector
333                         memcpy( &iface->IP4.Address, Data, sizeof(tIPv4) );
334                         iface->Type = 4;
335                         LEAVE_RET('i', 1);
336                 case 6:
337                         if( !CheckMem( Data, sizeof(tIPv6) ) )  LEAVE_RET('i', -1);
338                         iface->Type = 0;
339                         memcpy( &iface->IP6.Address, Data, sizeof(tIPv6) );
340                         iface->Type = 6;
341                         LEAVE_RET('i', 1);
342                 }
343                 LEAVE_RET('i', 0);
344         
345         /*
346          * getset_subnet
347          * - Get/Set the bits in the address subnet
348          */
349         case 7:
350                 // Get?
351                 if( Data == NULL )
352                 {
353                         switch( iface->Type )
354                         {
355                         case 4:         LEAVE_RET('i', iface->IP4.SubnetBits);
356                         case 6:         LEAVE_RET('i', iface->IP6.SubnetBits);
357                         default:        LEAVE_RET('i', 0);
358                         }
359                 }
360                 
361                 // Ok, set.
362                 if( Threads_GetUID() != 0 )     LEAVE_RET('i', -1);
363                 if( !CheckMem(Data, sizeof(int)) )      LEAVE_RET('i', -1);
364                 
365                 // Check and set the subnet bits
366                 switch( iface->Type )
367                 {
368                 case 4:
369                         if( *(int*)Data < 0 || *(int*)Data > 31 )       LEAVE_RET('i', -1);
370                         iface->IP4.SubnetBits = *(int*)Data;
371                         LEAVE_RET('i', iface->IP4.SubnetBits);
372                 case 6:
373                         if( *(int*)Data < 0 || *(int*)Data > 127 )      LEAVE_RET('i', -1);
374                         iface->IP6.SubnetBits = *(int*)Data;
375                         LEAVE_RET('i', iface->IP6.SubnetBits);
376                 default:
377                         break;
378                 }
379                 
380                 LEAVE('i', 0);
381                 return 0;
382                 
383         /*
384          * get_gateway
385          * - Get the interface's IPv4 gateway
386          */
387         case 8:
388                 switch(iface->Type)
389                 {
390                 case 0:
391                         LEAVE_RET('i', 1);
392                 case 4:
393                         if( !CheckMem( Data, sizeof(tIPv4) ) )  LEAVE_RET('i', -1);
394                         memcpy( Data, &iface->IP4.Gateway, sizeof(tIPv4) );
395                         LEAVE_RET('i', 1);
396                 case 6:
397                         LEAVE_RET('i', 1);
398                 }
399                 LEAVE('i', 0);
400                 return 0;
401         
402         /*
403          * set_gateway
404          * - Get/Set the interface's IPv4 gateway
405          */
406         case 9:
407                 if( Threads_GetUID() != 0 )     LEAVE_RET('i', -1);
408                 switch(iface->Type)
409                 {
410                 case 0:
411                         LEAVE_RET('i', 1);
412                 
413                 case 4:
414                         if( !CheckMem( Data, sizeof(tIPv4) ) )  LEAVE_RET('i', -1);
415                         iface->Type = 0;        // One very hacky mutex/trash protector
416                         memcpy( &iface->IP4.Gateway, Data, sizeof(tIPv4) );
417                         iface->Type = 4;
418                         LEAVE_RET('i', 1);
419                         
420                 case 6:
421                         LEAVE_RET('i', 1);
422                 }
423                 break;
424         
425         /*
426          * ping
427          * - Send an ICMP Echo
428          */
429         case 10:
430                 switch(iface->Type)
431                 {
432                 case 0:
433                         LEAVE_RET('i', 1);
434                 
435                 case 4:
436                         if( !CheckMem( Data, sizeof(tIPv4) ) )  LEAVE_RET('i', -1);
437                         tmp = IPv4_Ping(iface, *(tIPv4*)Data);
438                         LEAVE('i', tmp);
439                         return tmp;
440                         
441                 case 6:
442                         LEAVE_RET('i', 1);
443                 }
444                 break;
445         
446         }
447         
448         LEAVE('i', 0);
449         return 0;
450 }
451
452 // --- Internal ---
453 /**
454  * \fn int IPStack_AddInterface(char *Device)
455  * \brief Adds an interface to the list
456  */
457 int IPStack_AddInterface(char *Device)
458 {
459         tInterface      *iface;
460         
461         ENTER("sDevice", Device);
462         
463         iface = malloc(sizeof(tInterface));
464         if(!iface) {
465                 LEAVE('i', -2);
466                 return -2;      // Return ERR_MYBAD
467         }
468         
469         iface->Next = NULL;
470         iface->Type = 0;        // Unset type
471         
472         // Create Node
473         iface->Node.ImplPtr = iface;
474         iface->Node.ImplInt = giIP_NextIfaceId++;
475         iface->Node.Flags = VFS_FFLAG_DIRECTORY;
476         iface->Node.Size = 0;
477         iface->Node.NumACLs = 1;
478         iface->Node.ACLs = &gVFS_ACL_EveryoneRX;
479         iface->Node.ReadDir = NULL;
480         iface->Node.FindDir = NULL;
481         iface->Node.IOCtl = IPStack_IOCtl;
482         
483         // Set Defaults
484         iface->TimeoutDelay = DEFAULT_TIMEOUT;
485         
486         // Get adapter handle
487         iface->Adapter = IPStack_GetAdapter(Device);
488         if( !iface->Adapter ) {
489                 free( iface );
490                 LEAVE('i', -1);
491                 return -1;      // Return ERR_YOUFAIL
492         }
493         
494         // Append to list
495         LOCK( &glIP_Interfaces );
496         if( gIP_Interfaces ) {
497                 gIP_Interfaces_Last->Next = iface;
498                 gIP_Interfaces_Last = iface;
499         }
500         else {
501                 gIP_Interfaces = iface;
502                 gIP_Interfaces_Last = iface;
503         }
504         RELEASE( &glIP_Interfaces );
505         
506         gIP_DriverInfo.RootNode.Size ++;
507         
508         // Success!
509         LEAVE('i', iface->Node.ImplInt);
510         return iface->Node.ImplInt;
511 }
512
513 /**
514  * \fn tAdapter *IPStack_GetAdapter(char *Path)
515  * \brief Gets/opens an adapter given the path
516  */
517 tAdapter *IPStack_GetAdapter(char *Path)
518 {
519         tAdapter        *dev;
520          int    tmp;
521         
522         ENTER("sPath", Path);
523         
524         LOCK( &glIP_Adapters );
525         
526         // Check if this adapter is already open
527         for( dev = gIP_Adapters; dev; dev = dev->Next )
528         {
529                 if( strcmp(dev->Device, Path) == 0 ) {
530                         dev->NRef ++;
531                         RELEASE( &glIP_Adapters );
532                         LEAVE('p', dev);
533                         return dev;
534                 }
535         }
536         
537         // Ok, so let's open it
538         dev = malloc( sizeof(tAdapter) + strlen(Path) + 1 );
539         if(!dev) {
540                 RELEASE( &glIP_Adapters );
541                 LEAVE('n');
542                 return NULL;
543         }
544         
545         // Fill Structure
546         strcpy( dev->Device, Path );
547         dev->NRef = 1;
548         
549         // Open Device
550         dev->DeviceFD = VFS_Open( dev->Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE );
551         if( dev->DeviceFD == -1 ) {
552                 free( dev );
553                 RELEASE( &glIP_Adapters );
554                 LEAVE('n');
555                 return NULL;
556         }
557         
558         // Check that it is a network interface
559         tmp = VFS_IOCtl(dev->DeviceFD, 0, NULL);
560         LOG("Device type = %i", tmp);
561         if( tmp != DRV_TYPE_NETWORK ) {
562                 Warning("IPStack_GetAdapter: '%s' is not a network interface", dev->Device);
563                 VFS_Close( dev->DeviceFD );
564                 free( dev );
565                 RELEASE( &glIP_Adapters );
566                 LEAVE('n');
567                 return NULL;
568         }
569         
570         // Get MAC Address
571         VFS_IOCtl(dev->DeviceFD, NET_IOCTL_GETMAC, &dev->MacAddr);
572         
573         // Add to list
574         dev->Next = gIP_Adapters;
575         gIP_Adapters = dev;
576         
577         RELEASE( &glIP_Adapters );
578         
579         // Start watcher
580         Link_WatchDevice( dev );
581         
582         LEAVE('p', dev);
583         return dev;
584 }

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