Restructured interface code out to its own file, route search implemented
[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          int    i, num;
126         tInterface      *iface;
127         
128         ENTER("pNode sName", Node, Name);
129         
130         // Routing subdir
131         if( strcmp(Name, "routes") == 0 ) {
132                 return &gIP_RouteNode;
133         }
134         
135         // Loopback
136         if( strcmp(Name, "lo") == 0 ) {
137                 return &gIP_LoopInterface.Node;
138         }
139         
140         i = 0;  num = 0;
141         while('0' <= Name[i] && Name[i] <= '9')
142         {
143                 num *= 10;
144                 num += Name[i] - '0';
145                 i ++;
146         }
147         if(Name[i] != '\0') {
148                 LEAVE('n');
149                 return NULL;
150         }
151         
152         for( iface = gIP_Interfaces; iface; iface = iface->Next )
153         {
154                 if( (int)iface->Node.ImplInt == num )
155                 {
156                         LEAVE('p', &iface->Node);
157                         return &iface->Node;
158                 }
159         }
160         LEAVE('p', NULL);
161         return NULL;
162 }
163
164 static const char *casIOCtls_Root[] = { DRV_IOCTLNAMES, "add_interface", NULL };
165 /**
166  * \brief Handles IOCtls for the IPStack root
167  */
168 int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data)
169 {
170          int    tmp;
171         ENTER("pNode iID pData", Node, ID, Data);
172         
173         switch(ID)
174         {
175         // --- Standard IOCtls (0-3) ---
176         case DRV_IOCTL_TYPE:
177                 LEAVE('i', DRV_TYPE_MISC);
178                 return DRV_TYPE_MISC;
179         
180         case DRV_IOCTL_IDENT:
181                 tmp = ModUtil_SetIdent(Data, "IPStack");
182                 LEAVE('i', 1);
183                 return 1;
184         
185         case DRV_IOCTL_VERSION:
186                 LEAVE('x', VERSION);
187                 return VERSION;
188         
189         case DRV_IOCTL_LOOKUP:
190                 tmp = ModUtil_LookupString( (char**)casIOCtls_Root, (char*)Data );
191                 LEAVE('i', tmp);
192                 return tmp;
193                 
194                 /*
195                  * add_interface
196                  * - Adds a new IP interface and binds it to a device
197                  */
198         case 4:
199                 if( Threads_GetUID() != 0 )     LEAVE_RET('i', -1);
200                 if( !CheckString( Data ) )      LEAVE_RET('i', -1);
201                 {
202                         char    name[4] = "";
203                         tmp = IPStack_AddInterface(Data, name);
204                 }
205                 LEAVE_RET('i', tmp);
206         }
207         LEAVE('i', 0);
208         return 0;
209 }
210
211 /**
212  * \fn int IPStack_AddInterface(char *Device)
213  * \brief Adds an interface to the list
214  */
215 int IPStack_AddInterface(const char *Device, const char *Name)
216 {
217         tInterface      *iface;
218         tAdapter        *card;
219         
220         ENTER("sDevice", Device);
221         
222         card = IPStack_GetAdapter(Device);
223         
224         iface = malloc(sizeof(tInterface) + strlen(Name));
225         if(!iface) {
226                 LEAVE('i', -2);
227                 return -2;      // Return ERR_MYBAD
228         }
229         
230         iface->Next = NULL;
231         iface->Type = 0;        // Unset type
232         
233         // Create Node
234         iface->Node.ImplPtr = iface;
235         iface->Node.Flags = VFS_FFLAG_DIRECTORY;
236         iface->Node.Size = -1;
237         iface->Node.NumACLs = 1;
238         iface->Node.ACLs = &gVFS_ACL_EveryoneRX;
239         iface->Node.ReadDir = IPStack_Iface_ReadDir;
240         iface->Node.FindDir = IPStack_Iface_FindDir;
241         iface->Node.IOCtl = IPStack_Iface_IOCtl;
242         iface->Node.MkNod = NULL;
243         iface->Node.Link = NULL;
244         iface->Node.Relink = NULL;
245         iface->Node.Close = NULL;
246         
247         // Set Defaults
248         iface->TimeoutDelay = DEFAULT_TIMEOUT;
249         
250         // Get adapter handle
251         iface->Adapter = IPStack_GetAdapter(Device);
252         if( !iface->Adapter ) {
253                 free( iface );
254                 LEAVE('i', -1);
255                 return -1;      // Return ERR_YOUFAIL
256         }
257         
258         // Delay setting ImplInt until after the adapter is opened
259         // Keeps things simple
260         iface->Node.ImplInt = giIP_NextIfaceId++;
261         
262         // Append to list
263         SHORTLOCK( &glIP_Interfaces );
264         if( gIP_Interfaces ) {
265                 gIP_Interfaces_Last->Next = iface;
266                 gIP_Interfaces_Last = iface;
267         }
268         else {
269                 gIP_Interfaces = iface;
270                 gIP_Interfaces_Last = iface;
271         }
272         SHORTREL( &glIP_Interfaces );
273
274 //      gIP_DriverInfo.RootNode.Size ++;
275         
276         // Success!
277         LEAVE('i', iface->Node.ImplInt);
278         return iface->Node.ImplInt;
279 }
280
281 /**
282  * \brief Adds a file to the socket list
283  */
284 int IPStack_AddFile(tSocketFile *File)
285 {
286         Log_Log("IPStack", "Added file '%s'", File->Name);
287         File->Next = gIP_FileTemplates;
288         gIP_FileTemplates = File;
289         return 0;
290 }
291
292 // ---
293 // VFS Functions
294 // ---
295 /**
296  * \brief Read from an interface's directory
297  */
298 char *IPStack_Iface_ReadDir(tVFS_Node *Node, int Pos)
299 {
300         tSocketFile     *file = gIP_FileTemplates;
301         while(Pos-- && file) {
302                 file = file->Next;
303         }
304         
305         if(!file)       return NULL;
306         
307         return strdup(file->Name);
308 }
309
310 /**
311  * \brief Gets a named node from an interface directory
312  */
313 tVFS_Node *IPStack_Iface_FindDir(tVFS_Node *Node, const char *Name)
314 {
315         tSocketFile     *file = gIP_FileTemplates;
316         
317         // Get file definition
318         for(;file;file = file->Next)
319         {
320                 if( strcmp(file->Name, Name) == 0 )     break;
321         }
322         if(!file)       return NULL;
323         
324         // Pass the buck!
325         return file->Init(Node->ImplPtr);
326 }
327
328 /**
329  * \brief Names for interface IOCtl Calls
330  */
331 static const char *casIOCtls_Iface[] = {
332         DRV_IOCTLNAMES,
333         "getset_type",
334         "get_address", "set_address",
335         "getset_subnet",
336         "get_gateway", "set_gateway",
337         "get_device",
338         "ping",
339         NULL
340         };
341 /**
342  * \brief Handles IOCtls for the IPStack interfaces
343  */
344 int IPStack_Iface_IOCtl(tVFS_Node *Node, int ID, void *Data)
345 {
346          int    tmp;
347         tInterface      *iface = (tInterface*)Node->ImplPtr;
348         ENTER("pNode iID pData", Node, ID, Data);
349         
350         switch(ID)
351         {
352         // --- Standard IOCtls (0-3) ---
353         case DRV_IOCTL_TYPE:
354                 LEAVE('i', DRV_TYPE_MISC);
355                 return DRV_TYPE_MISC;
356         
357         case DRV_IOCTL_IDENT:
358                 tmp = ModUtil_SetIdent(Data, STR(IDENT));
359                 LEAVE('i', 1);
360                 return 1;
361         
362         case DRV_IOCTL_VERSION:
363                 LEAVE('x', VERSION);
364                 return VERSION;
365         
366         case DRV_IOCTL_LOOKUP:
367                 tmp = ModUtil_LookupString( (char**)casIOCtls_Iface, (char*)Data );
368                 LEAVE('i', tmp);
369                 return tmp;
370         
371         /*
372          * getset_type
373          * - Get/Set the interface type
374          */
375         case 4:
376                 // Set Type?
377                 if( Data )
378                 {
379                         // Ok, it's set type
380                         if( Threads_GetUID() != 0 ) {
381                                 LOG("Attempt by non-root to alter an interface (%i)", Threads_GetUID());
382                                 LEAVE('i', -1);
383                                 return -1;
384                         }
385                         if( !CheckMem( Data, sizeof(int) ) ) {
386                                 LOG("Invalid pointer %p", Data);
387                                 LEAVE('i', -1);
388                                 return -1;
389                         }
390                         switch( *(int*)Data )
391                         {
392                         case 0: // Disable
393                                 iface->Type = 0;
394                                 memset(&iface->IP6, 0, sizeof(tIPv6));  // Clear address
395                                 break;
396                         case 4: // IPv4
397                                 iface->Type = 4;
398                                 memset(&iface->IP4, 0, sizeof(tIPv4));
399                                 break;
400                         case 6: // IPv6
401                                 iface->Type = 6;
402                                 memset(&iface->IP6, 0, sizeof(tIPv6));
403                                 break;
404                         default:
405                                 LEAVE('i', -1);
406                                 return -1;
407                         }
408                 }
409                 LEAVE('i', iface->Type);
410                 return iface->Type;
411         
412         /*
413          * get_address
414          * - Get the interface's address
415          */
416         case 5:
417                 switch(iface->Type)
418                 {
419                 case 0: LEAVE_RET('i', 1);
420                 case 4:
421                         if( !CheckMem( Data, sizeof(tIPv4) ) )  LEAVE_RET('i', -1);
422                         memcpy( Data, &iface->IP4.Address, sizeof(tIPv4) );
423                         LEAVE_RET('i', 1);
424                 case 6:
425                         if( !CheckMem( Data, sizeof(tIPv6) ) )  LEAVE_RET('i', -1);
426                         memcpy( Data, &iface->IP6.Address, sizeof(tIPv6) );
427                         LEAVE_RET('i', 1);
428                 }
429                 LEAVE_RET('i', 0);
430         
431         /*
432          * set_address
433          * - Get the interface's address
434          */
435         case 6:
436                 if( Threads_GetUID() != 0 )     LEAVE_RET('i', -1);
437                 switch(iface->Type)
438                 {
439                 case 0: LEAVE_RET('i', 1);
440                 case 4:
441                         if( !CheckMem( Data, sizeof(tIPv4) ) )  LEAVE_RET('i', -1);
442                         iface->Type = 0;        // One very hacky mutex/trash protector
443                         memcpy( &iface->IP4.Address, Data, sizeof(tIPv4) );
444                         iface->Type = 4;
445                         LEAVE_RET('i', 1);
446                 case 6:
447                         if( !CheckMem( Data, sizeof(tIPv6) ) )  LEAVE_RET('i', -1);
448                         iface->Type = 0;
449                         memcpy( &iface->IP6.Address, Data, sizeof(tIPv6) );
450                         iface->Type = 6;
451                         LEAVE_RET('i', 1);
452                 }
453                 LEAVE_RET('i', 0);
454         
455         /*
456          * getset_subnet
457          * - Get/Set the bits in the address subnet
458          */
459         case 7:
460                 // Get?
461                 if( Data == NULL )
462                 {
463                         switch( iface->Type )
464                         {
465                         case 4:         LEAVE_RET('i', iface->IP4.SubnetBits);
466                         case 6:         LEAVE_RET('i', iface->IP6.SubnetBits);
467                         default:        LEAVE_RET('i', 0);
468                         }
469                 }
470                 
471                 // Ok, set.
472                 if( Threads_GetUID() != 0 )     LEAVE_RET('i', -1);
473                 if( !CheckMem(Data, sizeof(int)) )      LEAVE_RET('i', -1);
474                 
475                 // Check and set the subnet bits
476                 switch( iface->Type )
477                 {
478                 case 4:
479                         if( *(int*)Data < 0 || *(int*)Data > 31 )       LEAVE_RET('i', -1);
480                         iface->IP4.SubnetBits = *(int*)Data;
481                         LEAVE_RET('i', iface->IP4.SubnetBits);
482                 case 6:
483                         if( *(int*)Data < 0 || *(int*)Data > 127 )      LEAVE_RET('i', -1);
484                         iface->IP6.SubnetBits = *(int*)Data;
485                         LEAVE_RET('i', iface->IP6.SubnetBits);
486                 default:
487                         break;
488                 }
489                 
490                 LEAVE('i', 0);
491                 return 0;
492                 
493         /*
494          * get_gateway
495          * - Get the interface's IPv4 gateway
496          */
497         case 8:
498                 switch(iface->Type)
499                 {
500                 case 0:
501                         LEAVE_RET('i', 1);
502                 case 4:
503                         if( !CheckMem( Data, sizeof(tIPv4) ) )  LEAVE_RET('i', -1);
504                         memcpy( Data, &iface->IP4.Gateway, sizeof(tIPv4) );
505                         LEAVE_RET('i', 1);
506                 case 6:
507                         LEAVE_RET('i', 1);
508                 }
509                 LEAVE('i', 0);
510                 return 0;
511         
512         /*
513          * set_gateway
514          * - Get/Set the interface's IPv4 gateway
515          */
516         case 9:
517                 if( Threads_GetUID() != 0 )     LEAVE_RET('i', -1);
518                 switch(iface->Type)
519                 {
520                 case 0:
521                         LEAVE_RET('i', 1);
522                 
523                 case 4:
524                         if( !CheckMem( Data, sizeof(tIPv4) ) )  LEAVE_RET('i', -1);
525                         iface->Type = 0;        // One very hacky mutex/trash protector
526                         memcpy( &iface->IP4.Gateway, Data, sizeof(tIPv4) );
527                         iface->Type = 4;
528                         LEAVE_RET('i', 1);
529                         
530                 case 6:
531                         LEAVE_RET('i', 1);
532                 }
533                 break;
534         
535         /*
536          * get_device
537          * - Gets the name of the attached device
538          */
539         case 10:
540                 if( Data == NULL )
541                         LEAVE_RET('i', iface->Adapter->DeviceLen);
542                 if( !CheckMem( Data, iface->Adapter->DeviceLen+1 ) )
543                         LEAVE_RET('i', -1);
544                 strcpy( Data, iface->Adapter->Device );
545                 return iface->Adapter->DeviceLen;
546         
547         /*
548          * ping
549          * - Send an ICMP Echo
550          */
551         case 11:
552                 switch(iface->Type)
553                 {
554                 case 0:
555                         LEAVE_RET('i', 1);
556                 
557                 case 4:
558                         if( !CheckMem( Data, sizeof(tIPv4) ) )  LEAVE_RET('i', -1);
559                         tmp = IPv4_Ping(iface, *(tIPv4*)Data);
560                         LEAVE('i', tmp);
561                         return tmp;
562                         
563                 case 6:
564                         LEAVE_RET('i', 1);
565                 }
566                 break;
567         
568         }
569         
570         LEAVE('i', 0);
571         return 0;
572 }
573
574 // --- Internal ---
575 /**
576  * \fn tAdapter *IPStack_GetAdapter(const char *Path)
577  * \brief Gets/opens an adapter given the path
578  */
579 tAdapter *IPStack_GetAdapter(const char *Path)
580 {
581         tAdapter        *dev;
582          int    tmp;
583         
584         ENTER("sPath", Path);
585         
586         Mutex_Acquire( &glIP_Adapters );
587         
588         // Check if this adapter is already open
589         for( dev = gIP_Adapters; dev; dev = dev->Next )
590         {
591                 if( strcmp(dev->Device, Path) == 0 ) {
592                         dev->NRef ++;
593                         Mutex_Release( &glIP_Adapters );
594                         LEAVE('p', dev);
595                         return dev;
596                 }
597         }
598         
599         // Ok, so let's open it
600         dev = malloc( sizeof(tAdapter) + strlen(Path) + 1 );
601         if(!dev) {
602                 Mutex_Release( &glIP_Adapters );
603                 LEAVE('n');
604                 return NULL;
605         }
606         
607         // Fill Structure
608         strcpy( dev->Device, Path );
609         dev->NRef = 1;
610         dev->DeviceLen = strlen(Path);
611         
612         // Open Device
613         dev->DeviceFD = VFS_Open( dev->Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE );
614         if( dev->DeviceFD == -1 ) {
615                 free( dev );
616                 Mutex_Release( &glIP_Adapters );
617                 LEAVE('n');
618                 return NULL;
619         }
620         
621         // Check that it is a network interface
622         tmp = VFS_IOCtl(dev->DeviceFD, 0, NULL);
623         LOG("Device type = %i", tmp);
624         if( tmp != DRV_TYPE_NETWORK ) {
625                 Warning("IPStack_GetAdapter: '%s' is not a network interface", dev->Device);
626                 VFS_Close( dev->DeviceFD );
627                 free( dev );
628                 Mutex_Release( &glIP_Adapters );
629                 LEAVE('n');
630                 return NULL;
631         }
632         
633         // Get MAC Address
634         VFS_IOCtl(dev->DeviceFD, NET_IOCTL_GETMAC, &dev->MacAddr);
635         
636         // Add to list
637         dev->Next = gIP_Adapters;
638         gIP_Adapters = dev;
639         
640         Mutex_Release( &glIP_Adapters );
641         
642         // Start watcher
643         Link_WatchDevice( dev );
644         
645         LEAVE('p', dev);
646         return dev;
647 }

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