Merge branch 'master' of git://git.ucc.asn.au/tpg/acess2
[tpg/acess2.git] / Usermode / Applications / ifconfig_src / main.c
1 /*
2  * Acess2 IFCONFIG command
3  */
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <acess/sys.h>
9 #include <net.h>
10
11 // === CONSTANTS ===
12 #define FILENAME_MAX    255
13 #define IPSTACK_ROOT    "/Devices/ip"
14 #define DEFAULT_METRIC  30
15
16 // TODO: Move this to a header
17 #define ntohs(v)        (((v&0xFF)<<8)|((v>>8)&0xFF))
18
19 // === PROTOTYPES ===
20 void    PrintUsage(const char *ProgName);
21 void    DumpInterfaces(void);
22 void    DumpRoutes(void);
23 void    DumpInterface(const char *Name);
24 void    DumpRoute(const char *Name);
25  int    AddInterface(const char *Device);
26 void    AddRoute(const char *Interface, int AddressType, void *Dest, int MaskBits, int Metric, void *NextHop);
27  int    DoAutoConfig(const char *Device);
28  int    SetAddress(int IFNum, const char *Address);
29  int    ParseIPAddress(const char *Address, uint8_t *Dest, int *SubnetBits);
30
31 // === CODE ===
32 /**
33  * \brief Program entrypoint
34  */
35 int main(int argc, char *argv[])
36 {
37          int    ret;
38         
39         // No args, dump interfaces
40         if(argc == 1) {
41                 DumpInterfaces();
42                 return 0;
43         }
44         
45         // Routes
46         if( strcmp(argv[1], "route") == 0 )
47         {
48                 // Add new route
49                 if( argc > 2 && strcmp(argv[2], "add") == 0 )
50                 {
51                         uint8_t dest[16] = {0};
52                         uint8_t nextHop[16] = {0};
53                          int    addrType, subnetBits = -1;
54                          int    nextHopType, nextHopBits=-1;
55                         char    *ifaceName = NULL;
56                          int    metric = DEFAULT_METRIC;
57                         // Usage:
58                         // ifconfig route add <host>[/<prefix>] <interface> [<metric>]
59                         // ifconfig route add <host>[/<prefix>] <next hop> [<metric>]
60                         if( argc - 3  < 2 ) {
61                                 fprintf(stderr, "ERROR: '%s route add' takes at least two arguments, %i passed\n",
62                                         argv[0], argc-3);
63                                 PrintUsage(argv[0]);
64                                 return -1;
65                         }
66                         
67                         if( argc - 3 > 3 ) {
68                                 fprintf(stderr, "ERROR: '%s route add' takes at most three arguments, %i passed\n",
69                                         argv[0], argc-3);
70                                 PrintUsage(argv[0]);
71                                 return -1;
72                         }
73                         
74                         // Destination IP
75                         addrType = ParseIPAddress(argv[3], dest, &subnetBits);
76                         if( subnetBits == -1 ) {
77                                 subnetBits = Net_GetAddressSize(addrType)*8;
78                         }
79                         // Interface Name / Next Hop
80                         if( (nextHopType = ParseIPAddress(argv[4], nextHop, &nextHopBits)) == 0 )
81                         {
82                                 // Interface name
83                                 ifaceName = argv[4];
84                         }
85                         else
86                         {
87                                 // Next Hop
88                                 // - Check if it's the same type as the network/destination
89                                 if( nextHopType != addrType ) {
90                                         fprintf(stderr, "ERROR: Address type mismatch\n");
91                                         return -1;
92                                 }
93                                 // - Make sure there's no mask
94                                 if( nextHopBits != -1 ) {
95                                         fprintf(stderr, "Error: Next hop cannot be masked\n");
96                                         return -1;
97                                 }
98                         }
99                         
100                         // Metric
101                         if( argc - 3 >= 3 )
102                         {
103                                 metric = atoi(argv[5]);
104                                 if( metric == 0 && argv[5][0] != '0' ) {
105                                         fprintf(stderr, "ERROR: Metric should be a number\n");
106                                         return -1;
107                                 }
108                         }
109                         
110                         // Make the route!
111                         AddRoute(ifaceName, addrType, dest, subnetBits, metric, nextHop);
112                         
113                         return 0;
114                 }
115                 // Delete a route
116                 else if( argc > 2 && strcmp(argv[2], "del") == 0 )
117                 {
118                         // Usage:
119                         // ifconfig route del <routenum>
120                         // ifconfig route del <host>[/<prefix>]
121                 }
122                 else
123                 {
124                         // List routes
125                         DumpRoutes();
126                 }
127                 return 0;
128         }
129         // Add a new interface
130         else if( strcmp(argv[1], "add") == 0 )
131         {
132                 if( argc < 4 ) {
133                         fprintf(stderr, "ERROR: '%s add' requires two arguments, %i passed\n", argv[0], argc-2);
134                         PrintUsage(argv[0]);
135                         return -1;
136                 }
137                 // TODO: Also set the IP address as the usage says it does
138                 ret = AddInterface( argv[2] );
139                 if(ret < 0)     return ret;
140                 ret = SetAddress( ret, argv[3] );
141                 return ret;
142         }
143         // Delete an interface
144         else if( strcmp(argv[1], "del") == 0 )
145         {
146                 if( argc < 3 ) {
147                         fprintf(stderr, "ERROR: '%s del' requires an argument\n", argv[0]);
148                         PrintUsage(argv[0]);
149                         return -1;
150                 }
151                 // TODO:
152         }
153         // Autoconfigure an interface
154         // NOTE: Debugging hack (see the function for more details)
155         else if( strcmp(argv[1], "autoconf") == 0 )
156         {
157                 DoAutoConfig(argv[2]);
158                 return 0;
159         }
160         else if( strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 )
161         {
162                 PrintUsage(argv[0]);
163                 return 0;
164         }
165         
166         // Dump a named interface
167         DumpInterface(argv[1]);
168         
169         return 0;
170 }
171
172 /**
173  * \brief Print usage instructions
174  */
175 void PrintUsage(const char *ProgName)
176 {
177         fprintf(stderr, "Usage:\n");
178         fprintf(stderr, "    %s add <device> <ip>/<prefix>\n", ProgName);
179         fprintf(stderr, "        Add a new interface listening on <device> with the specified\n");
180         fprintf(stderr, "        address.\n");
181         fprintf(stderr, "    %s del <interface>\n", ProgName);
182         fprintf(stderr, "        Delete an interface\n");
183         fprintf(stderr, "    %s [<interface>]\n", ProgName);
184         fprintf(stderr, "        Print the current interfaces (or only <interface> if passed)\n");
185         fprintf(stderr, "\n");
186         fprintf(stderr, "    %s route\n", ProgName);
187         fprintf(stderr, "        Print the routing tables\n");
188         fprintf(stderr, "    %s route add <host>[/<prefix>] [<nexthop> OR <iface>] [<metric>]\n", ProgName);
189         fprintf(stderr, "        Add a new route\n");
190         fprintf(stderr, "    %s route del <host>[/<prefix>]\n", ProgName);
191         fprintf(stderr, "    %s route del <routenum>\n", ProgName);
192         fprintf(stderr, "        Add a new route\n");
193         fprintf(stderr, "\n");
194         fprintf(stderr, "A note on Acess's IP Stack:\n");
195         fprintf(stderr, "    Each interface corresponds to only one IP address (either IPv4\n");
196         fprintf(stderr, "    or IPv6). A network device can have multiple interfaces bound\n");
197         fprintf(stderr, "    to it, allowing multiple addresses for one network connection\n");
198         fprintf(stderr, "\n");
199 }
200
201 /**
202  * \brief Dump all interfaces
203  */
204 void DumpInterfaces(void)
205 {
206          int    dp;
207         char    filename[FILENAME_MAX+1];
208         
209         dp = open(IPSTACK_ROOT, OPENFLAG_READ);
210         
211         while( readdir(dp, filename) )
212         {
213                 if(filename[0] == '.')  continue;
214                 DumpInterface(filename);
215         }
216         
217         close(dp);
218 }
219
220 /**
221  * \brief Dump all interfaces
222  */
223 void DumpRoutes(void)
224 {
225          int    dp;
226         char    filename[FILENAME_MAX+1];
227         
228         dp = open(IPSTACK_ROOT"/routes", OPENFLAG_READ);
229         
230         printf("Type\tNetwork \tGateway \tMetric\tIFace\n");
231         
232         while( readdir(dp, filename) )
233         {
234                 if(filename[0] == '.')  continue;
235                 DumpRoute(filename);
236         }
237         
238         close(dp);
239 }
240
241 /**
242  * \brief Dump an interface
243  */
244 void DumpInterface(const char *Name)
245 {
246          int    fd;
247          int    type;
248         char    path[sizeof(IPSTACK_ROOT)+1+FILENAME_MAX+1] = IPSTACK_ROOT"/";
249         
250         strcat(path, Name);
251         
252         fd = open(path, OPENFLAG_READ);
253         if(fd == -1) {
254                 fprintf(stderr, "Bad interface name '%s' (%s does not exist)\t", Name, path);
255                 return ;
256         }
257         
258         type = ioctl(fd, 4, NULL);
259         
260         // Ignore -1 values
261         if( type == -1 ) {
262                 return ;
263         }
264         
265         printf("%s:\t", Name);
266         {
267                  int    call_num = ioctl(fd, 3, "get_device");
268                  int    len = ioctl(fd, call_num, NULL);
269                 char    *buf = malloc(len+1);
270                 ioctl(fd, call_num, buf);
271                 printf("'%s'\n", buf);
272                 free(buf);
273         }
274         printf("\t");
275         // Get the address type
276         switch(type)
277         {
278         case 0: // Disabled/Unset
279                 printf("DISABLED\n");
280                 break;
281         case 4: // IPv4
282                 {
283                 uint8_t ip[4];
284                  int    subnet;
285                 printf("IPv4\t");
286                 ioctl(fd, 5, ip);       // Get IP Address
287                 subnet = ioctl(fd, 7, NULL);    // Get Subnet Bits
288                 printf("%i.%i.%i.%i/%i\n", ip[0], ip[1], ip[2], ip[3], subnet);
289                 }
290                 break;
291         case 6: // IPv6
292                 {
293                 uint16_t        ip[8];
294                  int    subnet;
295                 printf("IPv6\t");
296                 ioctl(fd, 5, ip);       // Get IP Address
297                 subnet = ioctl(fd, 7, NULL);    // Get Subnet Bits
298                 printf("%x:%x:%x:%x:%x:%x:%x:%x/%i\n",
299                         ntohs(ip[0]), ntohs(ip[1]), ntohs(ip[2]), ntohs(ip[3]),
300                         ntohs(ip[4]), ntohs(ip[5]), ntohs(ip[6]), ntohs(ip[7]),
301                         subnet);
302                 }
303                 break;
304         default:        // Unknow
305                 printf("UNKNOWN (%i)\n", type);
306                 break;
307         }
308                         
309         close(fd);
310 }
311
312
313 /**
314  * \brief Dump a route
315  */
316 void DumpRoute(const char *Name)
317 {
318          int    fd;
319          int    type;
320         char    path[sizeof(IPSTACK_ROOT)+8+FILENAME_MAX+1] = IPSTACK_ROOT"/routes/";
321         
322         strcat(path, Name);
323         
324         fd = open(path, OPENFLAG_READ);
325         if(fd == -1) {
326                 printf("%s:\tUnable to open ('%s')\n", Name, path);
327                 return ;
328         }
329         
330          int    ofs = 2;
331         type = atoi(Name);
332         
333          int    i;
334          int    len = Net_GetAddressSize(type);
335         uint8_t net[len], gw[len];
336          int    subnet, metric;
337         for( i = 0; i < len; i ++ ) {
338                 char tmp[5] = "0x00";
339                 tmp[2] = Name[ofs++];
340                 tmp[3] = Name[ofs++];
341                 net[i] = atoi(tmp);
342         }
343         ofs ++;
344         subnet = atoi(Name+ofs);
345         ofs ++;
346         metric = atoi(Name+ofs);
347         ioctl(fd, ioctl(fd, 3, "get_nexthop"), gw);     // Get Gateway/NextHop
348         
349         // Get the address type
350         switch(type)
351         {
352         case 0: // Disabled/Unset
353                 printf("DISABLED\n");
354                 break;
355         case 4: // IPv4
356                 printf("IPv4\t");
357                 break;
358         case 6: // IPv6
359                 printf("IPv6\t");
360                 break;
361         default:        // Unknow
362                 printf("UNKNOWN (%i)\n", type);
363                 break;
364         }
365         printf("%s/%i\t", Net_PrintAddress(type, net), subnet);
366         printf("%s \t", Net_PrintAddress(type, gw));
367         printf("%i\t", metric);
368         
369         // Interface
370         {
371                  int    call_num = ioctl(fd, 3, "get_interface");
372                  int    len = ioctl(fd, call_num, NULL);
373                 char    *buf = malloc(len+1);
374                 ioctl(fd, call_num, buf);
375                 printf("'%s'\t", buf);
376                 free(buf);
377         }
378         
379         printf("\n");
380                         
381         close(fd);
382 }
383
384 /**
385  * \brief Create a new interface using the passed device
386  * \param Device        Network device to bind to
387  */
388 int AddInterface(const char *Device)
389 {
390          int    dp, ret;
391         
392         dp = open(IPSTACK_ROOT, OPENFLAG_READ);
393         ret = ioctl(dp, 4, (void*)Device);
394         close(dp);
395         
396         if( ret < 0 ) {
397                 fprintf(stderr, "Unable to add '%s' as a network interface\n", Device);
398                 return -1;
399         }
400         
401         printf("-- Added '"IPSTACK_ROOT"/%i' using device %s\n", ret, Device);
402         
403         return ret;
404 }
405
406 void AddRoute(const char *Interface, int AddressType, void *Dest, int MaskBits, int Metric, void *NextHop)
407 {
408          int    fd;
409          int    num;
410         char    *ifaceToFree = NULL;
411         
412         // Get interface name
413         if( !Interface )
414         {
415                 if( !NextHop ) {
416                         fprintf(stderr,
417                                 "BUG: AddRoute(Interface=NULL,...,NextHop=NULL)\n"
418                                 "Only one should be NULL\n"
419                                 );
420                         return ;
421                 }
422                 
423                 // Query for the interface name
424                 Interface = ifaceToFree = Net_GetInterface(AddressType, NextHop);
425         }
426         // Check address type (if the interface was passed)
427         // - If we got the interface name, then it should be correct
428         else
429         {
430                 char    ifacePath[sizeof(IPSTACK_ROOT"/")+strlen(Interface)+1];
431                 
432                 // Open interface
433                 strcpy(ifacePath, IPSTACK_ROOT"/");
434                 strcat(ifacePath, Interface);
435                 fd = open(ifacePath, 0);
436                 if( fd == -1 ) {
437                         fprintf(stderr, "Error: Interface '%s' does not exist\n", Interface);
438                         return ;
439                 }
440                 // Get and check type
441                 num = ioctl(fd, ioctl(fd, 3, "getset_type"), NULL);
442                 if( num != AddressType ) {
443                         fprintf(stderr, "Error: Passed type does not match interface type (%i != %i)\n",
444                                 AddressType, num);
445                         return ;
446                 }
447                 
448                 close(fd);
449         }
450         
451         // Create route
452          int    addrsize = Net_GetAddressSize(AddressType);
453          int    len = snprintf(NULL, 0, "/Devices/ip/routes/%i::%i:%i", AddressType, MaskBits, Metric) + addrsize*2;
454         char    path[len+1];
455         {
456                  int    i, ofs;
457                 ofs = sprintf(path, "/Devices/ip/routes/%i:", AddressType);
458                 for( i = 0; i < addrsize; i ++ )
459                         sprintf(path+ofs+i*2, "%02x", ((uint8_t*)Dest)[i]);
460                 ofs += addrsize*2;
461                 sprintf(path+ofs, ":%i:%i", MaskBits, Metric);
462         }
463
464         fd = open(path, 0);
465         if( fd != -1 ) {
466                 close(fd);
467                 fprintf(stderr, "Unable to create route '%s', already exists\n", path);
468                 return ;
469         }
470         fd = open(path, OPENFLAG_CREATE, 0);
471         if( fd == -1 ) {
472                 fprintf(stderr, "Unable to create '%s'\n", path);
473                 return ;
474         }
475         
476         if( NextHop )
477                 ioctl(fd, ioctl(fd, 3, "set_nexthop"), NextHop);
478         ioctl(fd, ioctl(fd, 3, "set_interface"), (void*)Interface);
479         
480         close(fd);
481         
482         // Check if the interface name was allocated by us
483         if( ifaceToFree )
484                 free(ifaceToFree);
485 }
486
487 /**
488  * \note Debugging HACK!
489  * \brief Autoconfigure the specified device to 10.0.2.55/24 using
490  *        10.0.2.2 as the gateway.
491  */
492 int DoAutoConfig(const char *Device)
493 {
494          int    tmp, fd;
495         char    path[sizeof(IPSTACK_ROOT)+1+4+1];       // /0000
496         uint8_t addr[4] = {10,0,2,55};
497         uint8_t gw[4] = {10,0,2,2};
498          int    subnet = 24;
499         
500         tmp = AddInterface(Device);
501         if( tmp < 0 )   return tmp;
502         
503         sprintf(path, IPSTACK_ROOT"/%i", tmp);
504         
505         fd = open(path, OPENFLAG_READ);
506         if( fd == -1 ) {
507                 fprintf(stderr, "Unable to open '%s'\n", path);
508                 return -1;
509         }
510         
511         tmp = 4;        // IPv4
512         tmp = ioctl(fd, ioctl(fd, 3, "getset_type"), &tmp);
513         if( tmp != 4 ) {
514                 fprintf(stderr, "Error in setting address type (got %i, expected 4)\n", tmp);
515                 return -1;
516         }
517         // Set Address
518         ioctl(fd, ioctl(fd, 3, "set_address"), addr);
519         // Set Subnet
520         ioctl(fd, ioctl(fd, 3, "getset_subnet"), &subnet);
521         
522         // Set routes
523         {
524                 uint8_t net[4] = {0,0,0,0};
525                 AddRoute(path + sizeof(IPSTACK_ROOT), 4, addr, subnet, DEFAULT_METRIC, net);    // This interface
526                 AddRoute(path + sizeof(IPSTACK_ROOT), 4, net, 0, DEFAULT_METRIC, gw);   // Gateway
527         }
528         
529         close(fd);
530         
531         printf("Set address to %i.%i.%i.%i/%i (GW: %i.%i.%i.%i)\n",
532                 addr[0], addr[1], addr[2], addr[3],
533                 subnet,
534                 gw[0], gw[1], gw[2], gw[3]);
535         
536         return 0;
537 }
538
539 /**
540  * \brief Set the address on an interface from a textual IP address
541  */
542 int     SetAddress(int IFNum, const char *Address)
543 {
544         uint8_t addr[16];
545          int    type;
546         char    path[sizeof(IPSTACK_ROOT)+1+5+1];       // ip000
547          int    tmp, fd, subnet;
548         
549         // Parse IP Address
550         type = ParseIPAddress(Address, addr, &subnet);
551         if(type == 0) {
552                 fprintf(stderr, "'%s' cannot be parsed as an IP address\n", Address);
553                 return -1;
554         }
555         
556         // Open file
557         sprintf(path, IPSTACK_ROOT"/%i", IFNum);
558         fd = open(path, OPENFLAG_READ);
559         if( fd == -1 ) {
560                 fprintf(stderr, "Unable to open '%s'\n", path);
561                 return -1;
562         }
563         
564         tmp = type;
565         tmp = ioctl(fd, ioctl(fd, 3, "getset_type"), &tmp);
566         if( tmp != type ) {
567                 fprintf(stderr, "Error in setting address type (got %i, expected %i)\n", tmp, type);
568                 close(fd);
569                 return -1;
570         }
571         // Set Address
572         ioctl(fd, ioctl(fd, 3, "set_address"), addr);
573         
574         // Set Subnet
575         ioctl(fd, ioctl(fd, 3, "getset_subnet"), &subnet);
576         
577         close(fd);
578         
579         // Dump!
580         //DumpInterface( path+sizeof(IPSTACK_ROOT)+1 );
581         
582         return 0;
583 }
584
585 /**
586  * \brief Parse an IP Address
587  * \return 0 for unknown, 4 for IPv4 and 6 for IPv6
588  */
589 int ParseIPAddress(const char *Address, uint8_t *Dest, int *SubnetBits)
590 {
591         const char      *p = Address;
592         
593         // Check first block
594         while(*p && *p >= '0' && *p <= '9')     p ++;
595         
596         // IPv4?
597         if(*p == '.')
598         {
599                  int    i = 0, j;
600                  int    val;
601                 
602                 for( j = 0; Address[i] && j < 4; j ++ )
603                 {
604                         val = 0;
605                         for( ; '0' <= Address[i] && Address[i] <= '9'; i++ )
606                         {
607                                 val = val*10 + Address[i] - '0';
608                         }
609                         if(val > 255) {
610                                 //printf("val > 255 (%i)\n", val);
611                                 return 0;
612                         }
613                         Dest[j] = val;
614                         
615                         if(Address[i] == '.')
616                                 i ++;
617                 }
618                 if( j != 4 ) {
619                         //printf("4 parts expected, %i found\n", j);
620                         return 0;
621                 }
622                 // Parse subnet size
623                 if(Address[i] == '/') {
624                         val = 0;
625                         i ++;
626                         while('0' <= Address[i] && Address[i] <= '9') {
627                                 val *= 10;
628                                 val += Address[i] - '0';
629                                 i ++;
630                         }
631                         if(val > 32) {
632                                 printf("Notice: Subnet size >32 (%i)\n", val);
633                         }
634                         if(SubnetBits)  *SubnetBits = val;
635                 }
636                 if(Address[i] != '\0') {
637                         //printf("EOS != '\\0', '%c'\n", Address[i]);
638                         return 0;
639                 }
640                 return 4;
641         }
642         
643         // IPv6
644         if(*p == ':' || ('a' <= *p && *p <= 'f') || ('A' <= *p && *p <= 'F'))
645         {
646                  int    i = 0;
647                  int    j, k;
648                  int    val, split = -1, end;
649                 uint16_t        hi[8], low[8];
650                 
651                 for( j = 0; Address[i] && j < 8; j ++ )
652                 {
653                         if(Address[i] == '/')
654                                 break;
655                         
656                         if(Address[i] == ':') {
657                                 if(split != -1) {
658                                         printf("Two '::'s\n");
659                                         return 0;
660                                 }
661                                 split = j;
662                                 i ++;
663                                 continue;
664                         }
665                         
666                         val = 0;
667                         for( k = 0; Address[i] && Address[i] != ':' && Address[i] != '/'; i++, k++ )
668                         {
669                                 val *= 16;
670                                 if('0' <= Address[i] && Address[i] <= '9')
671                                         val += Address[i] - '0';
672                                 else if('A' <= Address[i] && Address[i] <= 'F')
673                                         val += Address[i] - 'A' + 10;
674                                 else if('a' <= Address[i] && Address[i] <= 'f')
675                                         val += Address[i] - 'a' + 10;
676                                 else {
677                                         printf("%c unexpected\n", Address[i]);
678                                         return 0;
679                                 }
680                         }
681                         
682                         if(val > 0xFFFF) {
683                                 printf("val (0x%x) > 0xFFFF\n", val);
684                                 return 0;
685                         }
686                         
687                         if(split == -1)
688                                 hi[j] = val;
689                         else
690                                 low[j-split] = val;
691                         
692                         if( Address[i] == ':' ) {
693                                 i ++;
694                         }
695                 }
696                 end = j;
697                 
698                 // Parse subnet size
699                 if(Address[i] == '/') {
700                         val = 0;
701                         while('0' <= Address[i] && Address[i] <= '9') {
702                                 val *= 10;
703                                 val += Address[i] - '0';
704                                 i ++;
705                         }
706                         if(val > 128) {
707                                 printf("Notice: Subnet size >128 (%i)\n", val);
708                         }
709                         if(SubnetBits)  *SubnetBits = val;
710                 }
711                 
712                 for( j = 0; j < split; j ++ )
713                 {
714                         //printf("%04x:", hi[j]);
715                         Dest[j*2] = hi[j]>>8;
716                         Dest[j*2+1] = hi[j]&0xFF;
717                 }
718                 for( ; j < 8 - (end - split); j++ )
719                 {
720                         //printf("0000:", hi[j]);
721                         Dest[j*2] = 0;
722                         Dest[j*2+1] = 0;
723                 }
724                 for( k = 0; j < 8; j ++, k++)
725                 {
726                         //printf("%04x:", low[k]);
727                         Dest[j*2] = low[k]>>8;
728                         Dest[j*2+1] = low[k]&0xFF;
729                 }
730                 return 6;
731         }
732         // Unknown type
733         return 0;
734 }

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