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

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