Renamed tpl_drv_* to api_drv_* (a more fitting name)
[tpg/acess2.git] / Modules / IPStack / routing.c
1 /*
2  * Acess2 IP Stack
3  * - Routing Tables
4  */
5 #define DEBUG   0
6 #define VERSION VER2(0,10)
7 #include <acess.h>
8 #include <api_drv_common.h>
9 #include "ipstack.h"
10 #include "link.h"
11
12 #define DEFAUTL_METRIC  30
13
14 // === IMPORTS ===
15 extern tInterface       *gIP_Interfaces;
16 extern tVFS_Node        *IPStack_Root_FindDir(tVFS_Node *Node, const char *Filename);
17
18 // === PROTOTYPES ===
19 // - Routes directory
20 char    *IPStack_RouteDir_ReadDir(tVFS_Node *Node, int Pos);
21 tVFS_Node       *IPStack_RouteDir_FindDir(tVFS_Node *Node, const char *Name);
22  int    IPStack_RouteDir_IOCtl(tVFS_Node *Node, int ID, void *Data);
23 // - Route Management
24 tRoute  *IPStack_Route_Create(const char *InterfaceName);
25 tRoute  *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric);
26 tRoute  *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address);
27 // - Individual Routes
28  int    IPStack_Route_IOCtl(tVFS_Node *Node, int ID, void *Data);
29
30 // === GLOBALS ===
31  int    giIP_NextRouteId = 1;
32 tRoute  *gIP_Routes;
33 tRoute  *gIP_RoutesEnd;
34 tVFS_Node       gIP_RouteNode = {
35         .Flags = VFS_FFLAG_DIRECTORY,
36         .Size = -1,
37         .NumACLs = 1,
38         .ACLs = &gVFS_ACL_EveryoneRX,
39         .ReadDir = IPStack_RouteDir_ReadDir,
40         .FindDir = IPStack_RouteDir_FindDir,
41         .IOCtl = IPStack_RouteDir_IOCtl
42 };
43
44 // === CODE ===
45 /**
46  * \brief ReadDir for the /Devices/ip/routes/ directory
47  */
48 char *IPStack_RouteDir_ReadDir(tVFS_Node *Node, int Pos)
49 {
50         tRoute  *rt;
51         
52         for(rt = gIP_Routes; rt && Pos --; rt = rt->Next);
53         
54         if( !rt ) {
55                 return NULL;
56         }
57         
58         {
59                  int    len = sprintf(NULL, "%i", rt->Node.Inode);
60                 char    buf[len+1];
61                 sprintf(buf, "%i", rt->Node.Inode);
62                 return strdup(buf);
63         }
64 }
65
66 /**
67  * \brief FindDir for the /Devices/ip/routes/ directory
68  */
69 tVFS_Node *IPStack_RouteDir_FindDir(tVFS_Node *Node, const char *Name)
70 {
71          int    num = atoi(Name);
72         tRoute  *rt;
73         
74         // Zero is invalid, sorry :)
75         if( !num )      return NULL;
76         
77         // Interpret the name as <type>:<addr>, returning the interface for
78         // needed to access that address.
79         //   E.g. '4:0A02000A'  - 10.2.0.10
80         // Hm... It could do with a way to have a better address type representation
81         if( Name[1] == ':' )    // TODO: Allow variable length type codes
82         {
83                  int    addrSize = IPStack_GetAddressSize(num);
84                 Uint8   addrData[addrSize];
85                 
86                 // Errof if the size is invalid
87                 if( strlen(Name) != 2 + addrSize*2 )
88                         return NULL;
89                 
90                 // Parse the address
91                 // - Error if the address data is not fully hex
92                 if( UnHex(addrData, addrSize, Name + 2) != addrSize )
93                         return NULL;
94                 
95                 // Find the route
96                 rt = IPStack_FindRoute(num, NULL, addrData);
97                 if(!rt) return NULL;
98                 
99                 // Return the interface node
100                 // - Sure it's hijacking it from inteface.c's area, but it's
101                 //   simpler this way
102                 return &rt->Interface->Node;
103         }
104         
105         // The list is inherently sorted, so we can do a quick search
106         for(rt = gIP_Routes; rt && rt->Node.Inode < num; rt = rt->Next);
107         
108         // Fast fail end of list / larger number
109         if( !rt || rt->Node.Inode > num )
110                 return NULL;
111         
112         return &rt->Node;
113 }
114
115 /**
116  * \brief Names for the route list IOCtl Calls
117  */
118 static const char *casIOCtls_RouteDir[] = {
119         DRV_IOCTLNAMES,
120         "add_route",    // Add a route - char *InterfaceName
121         "locate_route", // Find the best route for an address - struct {int Type, char Address[]} *
122         NULL
123         };
124
125 /**
126  * \brief IOCtl for /Devices/ip/routes/
127  */
128 int IPStack_RouteDir_IOCtl(tVFS_Node *Node, int ID, void *Data)
129 {
130          int    tmp;
131         tRoute  *rt;
132         ENTER("pNode iID pData", Node, ID, Data);
133         switch(ID)
134         {
135         // --- Standard IOCtls (0-3) ---
136         BASE_IOCTLS(DRV_TYPE_MISC, STR(IDENT), VERSION, casIOCtls_RouteDir)
137         
138         case 4: // Add Route
139                 if( !CheckString(Data) )        LEAVE_RET('i', -1);
140                 rt = IPStack_Route_Create(Data);
141                 if( !rt )
142                         tmp = -1;
143                 else
144                         tmp = rt->Node.Inode;
145                 LEAVE('i', tmp);
146                 return tmp;
147         
148         case 5: // Locate Route
149                 {
150                         struct {
151                                  int    Type;
152                                 Uint8   Addr[];
153                         }       *data = Data;
154                         
155                         if( !CheckMem(Data, sizeof(int)) )
156                                 LEAVE_RET('i', -1);
157                         if( !CheckMem(Data, sizeof(int) + IPStack_GetAddressSize(data->Type)) )
158                                 LEAVE_RET('i', -1);
159                         
160                         Log_Debug("IPStack", "Route_RouteDir_IOCtl - FindRoute %i, %s",
161                                 data->Type, IPStack_PrintAddress(data->Type, data->Addr) );
162                         rt = IPStack_FindRoute(data->Type, NULL, data->Addr);
163                         
164                         if( !rt )
165                                 LEAVE_RET('i', 0);
166                         
167                         LEAVE('i', rt->Node.Inode);
168                         return rt->Node.Inode;
169                 }
170                 break;
171         }
172         LEAVE('i', 0);
173         return 0;
174 }
175
176 /**
177  * \brief Create a new route entry
178  * \param InterfaceName Name of the interface using this route
179  */
180 tRoute *IPStack_Route_Create(const char *InterfaceName)
181 {
182         tRoute  *rt;
183         tInterface      *iface;
184          int    size;
185         
186         // Get interface
187         // Note: Oh man! This is such a hack
188         {
189                 tVFS_Node       *node = IPStack_Root_FindDir(NULL, InterfaceName);
190                 if( !node ) {
191                         Log_Debug("IPStack", "IPStack_Route_Create - Unknown interface '%s'\n", InterfaceName);
192                         return NULL;
193                 }
194                 iface = node->ImplPtr;
195                 if(node->Close) node->Close(node);
196         }
197         
198         // Get the size of the specified address type
199         size = IPStack_GetAddressSize(iface->Type);
200         if( size == 0 ) {
201                 return NULL;
202         }
203         
204         // Allocate space
205         rt = calloc(1, sizeof(tRoute) + size*2 );
206         
207         // Set up node
208         rt->Node.ImplPtr = rt;
209         rt->Node.Inode = giIP_NextRouteId ++;
210         rt->Node.Size = 0;
211         rt->Node.NumACLs = 1,
212         rt->Node.ACLs = &gVFS_ACL_EveryoneRO;
213         rt->Node.IOCtl = IPStack_Route_IOCtl;
214         
215         // Set up state
216         rt->AddressType = iface->Type;
217         rt->Network = (void *)( (tVAddr)rt + sizeof(tRoute) );
218         rt->SubnetBits = 0;
219         rt->NextHop = (void *)( (tVAddr)rt + sizeof(tRoute) + size );
220         rt->Interface = iface;
221         rt->Metric = DEFAUTL_METRIC;
222         memset(rt->Network, 0, size);
223         memset(rt->NextHop, 0, size);
224         
225         // Add to list
226         if( gIP_RoutesEnd ) {
227                 gIP_RoutesEnd->Next = rt;
228                 gIP_RoutesEnd = rt;
229         }
230         else {
231                 gIP_Routes = gIP_RoutesEnd = rt;
232         }
233         
234         Log_Log("IPStack", "Route entry for '%s' created", InterfaceName);
235         
236         return rt;
237 }
238
239 /**
240  * \brief Add and fill a route
241  */
242 tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric)
243 {
244         tRoute  *rt = IPStack_Route_Create(Interface);
245          int    addrSize;
246         
247         if( !rt )       return NULL;
248         
249         addrSize = IPStack_GetAddressSize(rt->Interface->Type);
250         
251         memcpy(rt->Network, Network, addrSize);
252         if( NextHop )
253                 memcpy(rt->NextHop, NextHop, addrSize);
254         rt->SubnetBits = SubnetBits;
255         if( Metric )
256                 rt->Metric = Metric;
257         
258         return rt;
259 }
260
261 /**
262  */
263 tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address)
264 {
265         tRoute  *rt;
266         tRoute  *best = NULL;
267         tInterface      *iface;
268          int    addrSize;
269         
270         ENTER("iAddressType pInterface sAddress",
271                 AddressType, Interface, IPStack_PrintAddress(AddressType, Address));
272         
273         if( Interface && AddressType != Interface->Type ) {
274                 LOG("Interface->Type (%i) != AddressType", Interface->Type);
275                 LEAVE('n');
276                 return NULL;
277         }
278         
279         // Get address size
280         addrSize = IPStack_GetAddressSize(AddressType);
281         
282         // Check against explicit routes
283         for( rt = gIP_Routes; rt; rt = rt->Next )
284         {
285                 // Check interface
286                 if( Interface && rt->Interface != Interface )   continue;
287                 // Check address type
288                 if( rt->AddressType != AddressType )    continue;
289                 
290                 LOG("Checking network %s/%i", IPStack_PrintAddress(AddressType, rt->Network), rt->SubnetBits);
291                 
292                 // Check if the address matches
293                 if( !IPStack_CompareAddress(AddressType, rt->Network, Address, rt->SubnetBits) )
294                         continue;
295                 
296                 if( best ) {
297                         // More direct routes are preferred
298                         if( best->SubnetBits > rt->SubnetBits ) {
299                                 LOG("Skipped - less direct (%i < %i)", rt->SubnetBits, best->SubnetBits);
300                                 continue;
301                         }
302                         // If equally direct, choose the best metric
303                         if( best->SubnetBits == rt->SubnetBits && best->Metric < rt->Metric ) {
304                                 LOG("Skipped - higher metric (%i > %i)", rt->Metric, best->Metric);
305                                 continue;
306                         }
307                 }
308                 
309                 best = rt;
310         }
311         
312         // Check against implicit routes
313         if( !best && !Interface )
314         {
315                 for( iface = gIP_Interfaces; iface; iface = iface->Next )
316                 {
317                         if( Interface && iface != Interface )   continue;
318                         if( iface->Type != AddressType )        continue;
319                         
320                         
321                         // Check if the address matches
322                         if( !IPStack_CompareAddress(AddressType, iface->Address, Address, iface->SubnetBits) )
323                                 continue;
324                         
325                         if( best ) {
326                                 // More direct routes are preferred
327                                 if( best->SubnetBits > rt->SubnetBits ) {
328                                         LOG("Skipped - less direct (%i < %i)", rt->SubnetBits, best->SubnetBits);
329                                         continue;
330                                 }
331                                 // If equally direct, choose the best metric
332                                 if( best->SubnetBits == rt->SubnetBits && best->Metric < rt->Metric ) {
333                                         LOG("Skipped - higher metric (%i > %i)", rt->Metric, best->Metric);
334                                         continue;
335                                 }
336                         }
337                         
338                         rt = &iface->Route;
339                         memcpy(rt->Network, iface->Address, addrSize);
340                         memset(rt->NextHop, 0, addrSize);
341                         rt->Metric = DEFAUTL_METRIC;
342                         rt->SubnetBits = iface->SubnetBits;
343                         
344                         best = rt;
345                 }
346         }
347         if( !best && Interface )
348         {
349                 rt = &Interface->Route;
350                 // Make sure route is up to date
351                 memcpy(rt->Network, Interface->Address, addrSize);
352                 memset(rt->NextHop, 0, addrSize);
353                 rt->Metric = DEFAUTL_METRIC;
354                 rt->SubnetBits = Interface->SubnetBits;
355                 
356                 if( IPStack_CompareAddress(AddressType, rt->Network, Address, rt->SubnetBits) )
357                 {
358                         best = rt;
359                 }
360         }
361         
362         LEAVE('p', best);
363         return best;
364 }
365
366 /**
367  * \brief Names for route IOCtl Calls
368  */
369 static const char *casIOCtls_Route[] = {
370         DRV_IOCTLNAMES,
371         "get_type",     // Get address type - (void), returns integer type
372         "get_network",  // Get network - (void *Data), returns boolean success
373         "set_network",  // Set network - (void *Data), returns boolean success
374         "get_nexthop",  // Get next hop - (void *Data), returns boolean success
375         "set_nexthop",  // Set next hop - (void *Data), returns boolean success
376         "getset_subnetbits",    // Get/Set subnet bits - (int *Bits), returns current value
377         "getset_metric",        // Get/Set metric - (int *Metric), returns current value
378         "get_interface",        // Get interface name - (char *Name), returns name length, NULL OK
379         NULL
380         };
381
382 /**
383  * \brief IOCtl for /Devices/ip/routes/#
384  */
385 int IPStack_Route_IOCtl(tVFS_Node *Node, int ID, void *Data)
386 {
387          int    *iData = Data;
388         tRoute  *rt = Node->ImplPtr;
389          int    addrSize = IPStack_GetAddressSize(rt->AddressType);
390         
391         switch(ID)
392         {
393         // --- Standard IOCtls (0-3) ---
394         BASE_IOCTLS(DRV_TYPE_MISC, STR(IDENT), VERSION, casIOCtls_Route)
395         
396         // Get address type
397         case 4:
398                 return rt->AddressType;
399         
400         // Get Network
401         case 5:
402                 if( !CheckMem(Data, addrSize) ) return -1;
403                 memcpy(Data, rt->Network, addrSize);
404                 return 1;
405         // Set Network
406         case 6:
407                 if( !CheckMem(Data, addrSize) ) return -1;
408                 memcpy(rt->Network, Data, addrSize);
409                 return 1;
410         
411         // Get Next Hop
412         case 7:
413                 if( !CheckMem(Data, addrSize) ) return -1;
414                 memcpy(Data, rt->NextHop, addrSize);
415                 return 1;
416         // Set Next Hop
417         case 8:
418                 if( !CheckMem(Data, addrSize) ) return -1;
419                 memcpy(rt->NextHop, Data, addrSize);
420                 return 1;
421         
422         // Get/Set Subnet Bits
423         case 9:
424                 if( Data ) {
425                         if( !CheckMem(Data, sizeof(int)) )      return -1;
426                         if( *iData < 0 || *iData > addrSize*8 )
427                                 return -1;
428                         rt->SubnetBits = *iData;
429                 }
430                 return rt->SubnetBits;
431         
432         // Get/Set Metric
433         case 10:
434                 if( Data ) {
435                         if( !CheckMem(Data, sizeof(int)) )      return -1;
436                         if( *iData < 0 )        return -1;
437                         rt->Metric = *iData;
438                 }
439                 return rt->Metric;
440         
441         // Get interface name
442         case 11:
443                 if( Data ) {
444                         if( !CheckMem(Data, strlen(rt->Interface->Name) + 1) )
445                                 return -1;
446                         strcpy(Data, rt->Interface->Name);
447                 }
448                 return strlen(rt->Interface->Name);
449         
450         default:
451                 return -1;
452         }
453 }

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