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

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