Externals/cross-compiler - Patching to get things compiling
[tpg/acess2.git] / Usermode / Applications / dhcpclient_src / main.c
1 /*
2  * 
3  * http://www.ietf.org/rfc/rfc2131.txt
4  */
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <net.h>
10 #include <acess/sys.h>
11
12 #define FILENAME_MAX    255
13 // --- Translation functions ---
14 static inline uint32_t htonl(uint32_t v)
15 {
16         return    (((v >> 24) & 0xFF) <<  0)
17                 | (((v >> 16) & 0xFF) <<  8)
18                 | (((v >>  8) & 0xFF) << 16)
19                 | (((v >>  0) & 0xFF) << 24);
20 }
21 static inline uint16_t htons(uint16_t v)
22 {
23         return    (((v >> 8) & 0xFF) <<  0)
24                 | (((v >> 0) & 0xFF) <<  8);
25 }
26 #define htonb(v)        v
27 #define ntohl(v)        htonl(v)
28 #define ntohs(v)        htons(v)
29
30 // === CONSTANTS ===
31 enum eStates
32 {
33         STATE_PREINIT,
34         STATE_DISCOVER_SENT,
35         STATE_REQUEST_SENT,
36         STATE_COMPLETE,
37 };
38
39 // === STRUCTURES ===
40 #define DHCP_MAGIC      0x63825363
41 struct sDHCP_Message
42 {
43         uint8_t op;
44         uint8_t htype;  // 1 = Ethernet
45         uint8_t hlen;   // 6 bytes for MAC
46         uint8_t hops;   // Hop counting
47         uint32_t        xid;
48         uint16_t        secs;
49         uint16_t        flags;
50         uint32_t        ciaddr;
51         uint32_t        yiaddr;
52         uint32_t        siaddr;
53         uint32_t        giaddr;
54         uint8_t chaddr[16];
55         uint8_t sname[64];
56         uint8_t file[128];
57         uint32_t        dhcp_magic;     // 0x63 0x82 0x53 0x63
58         uint8_t options[];
59 };
60
61 typedef struct sInterface
62 {
63         struct sInterface       *Next;
64         char    *Adapter;
65          int    State;
66          int    Num;
67          int    SocketFD;
68          int    IfaceFD;
69
70         uint32_t        TransactionID;
71         char    HWAddr[6];
72         
73         int64_t Timeout;
74          int    nTimeouts;
75 } tInterface;
76
77 // === PROTOTYPES ===
78 int     main(int argc, char *argv[]);
79 void    Scan_Dir(tInterface **IfaceList, const char *Directory);
80 int     Start_Interface(tInterface *Iface);
81 void    Send_DHCPDISCOVER(tInterface *Iface);
82 void    Send_DHCPREQUEST(tInterface *Iface, void *OfferBuffer, int TypeOffset);
83  int    Handle_Packet(tInterface *Iface);
84  int    Handle_Timeout(tInterface *Iface);
85 void    Update_State(tInterface *Iface, int newState);
86 void    SetAddress(tInterface *Iface, void *Addr, void *Mask, void *Router);
87
88 // === CODE ===
89 int main(int argc, char *argv[])
90 {
91         tInterface      *ifaces = NULL, *i;
92
93         // TODO: Scan /Devices and search for network adapters
94         if( argc > 2 ) {
95                 fprintf(stderr, "Usage: %s <interface>\n", argv[0]);
96                 return -1;
97         }
98         
99         if( argc == 2 ) {
100                 ifaces = malloc(sizeof(tInterface));
101                 ifaces->Next = NULL;
102                 ifaces->Adapter = argv[1];
103         }
104         else {
105                 Scan_Dir( &ifaces, "/Devices/ip/adapters" );
106         }
107
108         for( i = ifaces; i; i = i->Next )
109         {
110                 if( Start_Interface(i) < 0 ) {
111                         return -1;
112                 }
113                 i->State = STATE_PREINIT;
114                 
115                 // Send request
116                 Send_DHCPDISCOVER(i);
117         }
118
119         while( ifaces ) 
120         {
121                  int    maxfd;
122                 fd_set  fds;
123                 tInterface      *p;
124                 int64_t timeout = 1000;
125         
126                 maxfd = 0;
127                 FD_ZERO(&fds);
128                 for( i = ifaces; i; i = i->Next )
129                 {
130                         FD_SET(i->SocketFD, &fds);
131                         if(maxfd < i->SocketFD) maxfd = i->SocketFD;
132                 }
133                 
134                 if( select(maxfd+1, &fds, NULL, NULL, &timeout) < 0 )
135                 {
136                         // TODO: Check error result
137                         return -1;
138                 }
139
140                 _SysDebug("select() returned");
141
142                 // Check for changes (with magic to allow inline deletion)
143                 for( p = (void*)&ifaces, i = ifaces; i; p=i,i = i->Next )
144                 {
145                         if( FD_ISSET(i->SocketFD, &fds) )
146                         {
147                                 if( Handle_Packet( i ) )
148                                         goto _remove;
149                         }
150                         
151                         if( _SysTimestamp() > i->Timeout )
152                         {
153                                 if( Handle_Timeout(i) )
154                                 {
155                                         fprintf(stderr, "%s timed out\n", i->Adapter);
156                                         goto _remove;
157                                 }
158                         }
159                         continue ;
160                 _remove:
161                         _SysClose(i->SocketFD);
162                         _SysClose(i->IfaceFD);
163                         p->Next = i->Next;
164                         free(i);
165                         i = p;
166                 }
167         }
168         return 0;
169 }
170
171 void Scan_Dir(tInterface **IfaceList, const char *Directory)
172 {
173         int dp = _SysOpen(Directory, OPENFLAG_READ);
174         char filename[FILENAME_MAX];
175
176         if( dp == -1 ) {
177                 fprintf(stderr, "Unable to open directory '%s'\n", Directory);
178                 return ;
179         }
180
181         while( _SysReadDir(dp, filename) )
182         {
183                 if( filename[0] == '.' )        continue ;              
184                 if( strcmp(filename, "lo") == 0 )       continue ;
185
186                 tInterface *new = malloc(sizeof(tInterface) + strlen(filename)+1);
187                 new->Adapter = (void*)(new + 1);
188                 strcpy(new->Adapter, filename);
189                 new->Next = *IfaceList;
190                 *IfaceList = new;
191         }
192         _SysClose(dp);
193 }
194
195 // RETURN: Client FD
196 int Start_Interface(tInterface *Iface)
197 {
198          int    fd, tmp;
199         char    path[] = "/Devices/ip/XXXXX/udpc";
200         char    addr[4] = {0,0,0,0};
201         
202         // TODO: Check that the adapter is not in use
203         
204         // Request MAC address from network adapter
205         {
206                 char    path[] = "/Devices/ip/adapters/ethXXXX";
207                 sprintf(path, "/Devices/ip/adapters/%s", Iface->Adapter);
208                 fd = _SysOpen(path, 0);
209                 if(fd == -1) {
210                         _SysDebug("Unable to open adapter %s", path);
211                         return -1;
212                 }
213                 _SysIOCtl(fd, 4, Iface->HWAddr);
214                 // TODO: Check if ioctl() failed
215                 _SysClose(fd);
216         }
217         
218         // Initialise an interface, with a dummy IP address (zero)
219         fd = _SysOpen("/Devices/ip", 0);
220         if( fd == -1 ) {
221                 fprintf(stderr, "ERROR: Unable to open '/Devices/ip'\n"); 
222                 return -1;
223         }
224         struct {
225                 const char *Device;
226                 const char *Name;
227                  int    Type;
228         } ifinfo;
229         ifinfo.Device = Iface->Adapter;
230         ifinfo.Name = "";
231         ifinfo.Type = 4;
232         Iface->Num = _SysIOCtl(fd, 4, &ifinfo); // Create interface
233         if( Iface->Num == -1 ) {
234                 fprintf(stderr, "ERROR: Unable to create new interface\n");
235                 return -1;
236         }
237         _SysClose(fd);
238         
239         // Open new interface
240         snprintf(path, sizeof(path), "/Devices/ip/%i", Iface->Num);
241         Iface->IfaceFD = fd = _SysOpen(path, 0);
242         if( fd == -1 ) {
243                 fprintf(stderr, "ERROR: Unable to open '%s'\n", path); 
244                 return -1;
245         }
246         _SysIOCtl(fd, 6, addr); // Set address to 0.0.0.0
247         tmp = 0; _SysIOCtl(fd, 7, &tmp);        // Set subnet mask to 0
248
249         // Open UDP Client
250         snprintf(path, sizeof(path), "/Devices/ip/%i/udp", Iface->Num);
251         Iface->SocketFD = fd = _SysOpen(path, OPENFLAG_READ|OPENFLAG_WRITE);
252         if( fd == -1 ) {
253                 fprintf(stderr, "ERROR: Unable to open '%s'\n", path); 
254                 return -1;
255         }
256         tmp = 68; _SysIOCtl(fd, 4, &tmp);       // Local port
257         tmp = 67; _SysIOCtl(fd, 5, &tmp);       // Remote port
258         tmp = 0;  _SysIOCtl(fd, 7, &tmp);       // Remote addr mask bits - we don't care where the reply comes from
259         addr[0] = addr[1] = addr[2] = addr[3] = 255;    // 255.255.255.255
260         _SysIOCtl(fd, 8, addr); // Remote address
261         
262         return 0;
263 }
264
265 void Send_DHCPRELEASE(tInterface *Iface)
266 {
267 }
268
269 void Send_DHCPDISCOVER(tInterface *Iface)
270 {
271         uint32_t        transaction_id;
272         struct sDHCP_Message    *msg;
273         char    data[8 + sizeof(struct sDHCP_Message) + 3 + 1];
274         msg = (void*)data + 8;
275         
276         _SysDebug("DHCPDISCOVER to %s", Iface->Adapter);
277
278         transaction_id = rand();
279         Iface->TransactionID = transaction_id;
280
281         msg->op    = htonb(1);  // BOOTREQUEST
282         msg->htype = htonb(1);  // 10mb Ethernet
283         msg->hlen  = htonb(6);  // 6 byte MAC
284         msg->hops  = htonb(0);  // Hop count so far
285         msg->xid   = htonl(transaction_id);     // Transaction ID
286         msg->secs  = htons(0);  // secs - No time has elapsed
287         msg->flags = htons(0x0000);     // flags - Broadcast is unset
288         msg->ciaddr = htonl(0); // ciaddr - Zero, as we don't have one yet
289         msg->yiaddr = htonl(0); // yiaddr - Zero?
290         msg->siaddr = htonl(0); // siaddr - Zero? maybe -1
291         msg->giaddr = htonl(0); // giaddr - Zero?
292         memcpy(msg->chaddr, Iface->HWAddr, 6);
293
294         memset(msg->sname, 0, sizeof(msg->sname));      // Nuke the rest
295         memset(msg->file, 0, sizeof(msg->file));        // Nuke the rest
296         msg->dhcp_magic = htonl(DHCP_MAGIC);
297
298         int i = 0;
299         msg->options[i++] =  53;        // DHCP Message Type
300         msg->options[i++] =   1;
301         msg->options[i++] =   1;        // - DHCPDISCOVER
302         msg->options[i++] = 255;        // End of list
303         
304
305         data[0] = 67;   data[1] = 0;    // Port
306         data[2] = 4;    data[3] = 0;    // AddrType
307         data[4] = 255;  data[5] = 255;  data[6] = 255;  data[7] = 255;
308
309         i = _SysWrite(Iface->SocketFD, data, sizeof(data));
310         if( i != sizeof(data) ) {
311                 _SysDebug("_SysWrite failed (%i != %i)", i, sizeof(data));
312         }
313         Update_State(Iface, STATE_DISCOVER_SENT);
314 }
315
316 void Send_DHCPREQUEST(tInterface *Iface, void *OfferPacket, int TypeOffset)
317 {
318         struct sDHCP_Message    *msg;
319          int    i;
320         msg = (void*) ((char*)OfferPacket) + 8;
321
322         // Reuses old data :)
323         msg->op    = 1;
324         msg->htype = 1;
325         msg->hlen  = 6;
326         msg->hops  = 0;
327         msg->xid   = msg->xid;
328         msg->secs  = htons(0);  // TODO: Maintain times
329         msg->flags = htons(0);
330         memcpy(msg->chaddr, Iface->HWAddr, 6);
331         memset(msg->sname, 0, sizeof(msg->sname));      // Nuke the rest
332         memset(msg->file, 0, sizeof(msg->file));        // Nuke the rest
333
334         i = 0;
335         msg->options[i++] = 53; // Message type = DHCPREQUEST
336         msg->options[i++] = 1;
337         msg->options[i++] = 3;
338         msg->options[i++] = 50; // Requested Address
339         msg->options[i++] = 4;
340         memcpy(msg->options + i, &msg->yiaddr, 4);      i += 4;
341 //      msg->options[i++] = 54; // Server identifier
342 //      msg->options[i++] = 4;
343 //      memcpy(msg->options + i, (char*)OfferPacket + 4, 4);    i += 4;
344         msg->options[i++] = 255;
345         
346         // Clear last because yiaddr is needed in option setup
347         msg->ciaddr = htonl(0);
348         msg->yiaddr = htonl(0);
349         msg->siaddr = htonl(0);
350         msg->giaddr = htonl(0);
351
352         // HACK
353         ((uint8_t*)OfferPacket)[4] = 255;
354         ((uint8_t*)OfferPacket)[5] = 255;
355         ((uint8_t*)OfferPacket)[6] = 255;
356         ((uint8_t*)OfferPacket)[7] = 255;
357         
358         _SysWrite(Iface->SocketFD, OfferPacket, 8 + sizeof(*msg) + i);
359         Update_State(Iface, STATE_REQUEST_SENT);
360 }
361
362 int Handle_Packet(tInterface *Iface)
363 {
364         char    data[8+576];
365         struct sDHCP_Message    *msg = (void*)(data + 8);
366          int    len, i;
367         
368          int    dhcp_msg_type = 0, dhcp_msg_type_ofs;
369         void    *router = NULL;
370         void    *subnet_mask = NULL;
371         
372         _SysDebug("Doing read on %i", Iface->SocketFD);
373         len = _SysRead(Iface->SocketFD, data, sizeof(data));
374         _SysDebug("len = %i", len);
375
376         _SysDebug("*msg = {");
377         _SysDebug("  .op = %i", msg->op);
378         _SysDebug("  .htype = %i", msg->htype);
379         _SysDebug("  .ciaddr = 0x%x", ntohl(msg->ciaddr));
380         _SysDebug("  .yiaddr = 0x%x", ntohl(msg->yiaddr));
381         _SysDebug("}");
382         if( msg->op != 2 ) {
383                 // Not a response
384                 _SysDebug("Not a response message");
385                 return 0;
386         }
387
388         if( htonl(msg->dhcp_magic) != DHCP_MAGIC ) {
389                 _SysDebug("DHCP magic doesn't match (got 0x%x, expected 0x%x)",
390                         htonl(msg->dhcp_magic), DHCP_MAGIC);
391                 return 0;
392         }       
393
394
395         // Check if the packet is related to our requests
396         if( ntohl(msg->xid) != Iface->TransactionID ) {
397                 _SysDebug("Transaction ID mis-match, ignoring (0x%x != 0x%x)",
398                         ntohl(msg->xid), Iface->TransactionID);
399                 return 0;
400         }
401         if( memcmp(msg->chaddr, Iface->HWAddr, 6) != 0 ) {
402                 _SysDebug("Hardware address mis-match, ignoring");
403                 return 0;
404         }
405
406         // Parse options
407         i = 0;
408         while( i < len - sizeof(*msg) - 8 && msg->options[i] != 255 )
409         {
410                 if( msg->options[i] == 0 ) {
411                         i ++;
412                         continue ;
413                 }
414                 _SysDebug("Option %i, %i bytes long", msg->options[i], msg->options[i+1]);
415                 switch(msg->options[i])
416                 {
417                 case 1:
418                         subnet_mask = &msg->options[i+2];
419                         break;
420                 case 3:
421                         router = &msg->options[i+2];
422                         break;
423                 case 53:
424                         dhcp_msg_type_ofs = i;
425                         dhcp_msg_type = msg->options[i+2];
426                         break;
427                 }
428                 i += msg->options[i+1]+2;
429         }
430         
431         _SysDebug("dhcp_msg_type = %i", dhcp_msg_type);
432
433         switch( dhcp_msg_type )
434         {
435         case 1: // DHCPDISCOVER - wut?
436                 break;
437         case 2: // DHCPOFFER
438                 // Send out request for this address
439                 if( Iface->State != STATE_DISCOVER_SENT ) {
440                         _SysDebug("Ignoring DHCPOFFER when not in STATE_DISCOVER_SENT");
441                         return 0;
442                 }
443                 Send_DHCPREQUEST(Iface, data, dhcp_msg_type_ofs);
444                 break;
445         case 3: // DHCPREQUEST - wut?
446                 break;
447         case 4: // DHCPDECLINE - ?
448                 break;
449         case 5: // DHCPACK
450                 SetAddress(Iface, &msg->yiaddr, subnet_mask, router);
451                 // Return 1 to remove from list
452                 return 1;
453         }
454         return 0;
455 }
456
457 int Handle_Timeout(tInterface *Iface)
458 {
459         if( Iface->nTimeouts == 3 )
460                 return 1;
461         switch(Iface->State)
462         {
463         case STATE_DISCOVER_SENT:
464                 Send_DHCPDISCOVER(Iface);
465                 break;
466         default:
467                 _SysDebug("Timeout with state = %i", Iface->State);
468                 break;
469         }
470         return 0;
471 }
472
473 void Update_State(tInterface *Iface, int newState)
474 {
475         if( Iface->State != newState )
476         {
477                 Iface->Timeout = _SysTimestamp() + 500;
478                 Iface->State = newState;
479                 Iface->nTimeouts = 0;
480         }
481         else
482         {
483                 // TODO: Exponential backoff
484                 Iface->Timeout = _SysTimestamp() + 3000;
485                 Iface->nTimeouts ++;
486                 _SysDebug("State %i repeated, timeout is 3000ms now", newState);
487         }
488 }
489
490 void SetAddress(tInterface *Iface, void *Addr, void *Mask, void *Router)
491 {
492          int    mask_bits = 0;  
493
494         // Translate the mask
495         if( Mask )
496         {
497                 uint8_t *mask = Mask;
498                  int    i;
499                 _SysDebug("Mask %i.%i.%i.%i", mask[0], mask[1], mask[2], mask[3]);
500                 for( i = 0; i < 4 && mask[i] == 0xFF; i ++ ) ;
501                 mask_bits = i*8;
502                 if( i == 4 )
503                 {
504                         // Wut? /32?
505                 }
506                 else
507                 {
508                         switch(mask[i])
509                         {
510                         case 0x00:      mask_bits += 0; break;
511                         case 0x80:      mask_bits += 1; break;
512                         case 0xC0:      mask_bits += 2; break;
513                         case 0xE0:      mask_bits += 3; break;
514                         case 0xF0:      mask_bits += 4; break;
515                         case 0xF8:      mask_bits += 5; break;
516                         case 0xFC:      mask_bits += 6; break;
517                         case 0xFE:      mask_bits += 7; break;
518                         default:
519                                 // Bad mask!
520                                 break;
521                         }
522                 }
523         }
524         
525         {
526                 uint8_t *addr = Addr;
527                 _SysDebug("Addr %i.%i.%i.%i/%i", addr[0], addr[1], addr[2], addr[3], mask_bits);
528
529                 printf("Assigned %i.%i.%i.%i/%i to IF#%i (%s)\n",
530                         addr[0], addr[1], addr[2], addr[3], mask_bits,
531                         Iface->Num, Iface->Adapter
532                         );
533         }
534
535         _SysIOCtl(Iface->IfaceFD, 6, Addr);
536         _SysIOCtl(Iface->IfaceFD, 7, &mask_bits);
537
538         if( Router )
539         {
540                 uint8_t *addr = Router;
541                 _SysDebug("Router %i.%i.%i.%i", addr[0], addr[1], addr[2], addr[3]);
542                 
543                 // Set default route
544                  int    fd;
545                 fd = _SysOpen("/Devices/ip/routes/4:00000000:0:0", OPENFLAG_CREATE);
546                 if(fd == -1) {
547                         fprintf(stderr, "ERROR: Unable to open default route\n");
548                 }
549                 else {
550                         char ifname[snprintf(NULL,0,"%i",Iface->Num)+1];
551                         sprintf(ifname, "%i", Iface->Num);
552                         _SysIOCtl(fd, _SysIOCtl(fd, 3, "set_nexthop"), Router);
553                         _SysIOCtl(fd, _SysIOCtl(fd, 3, "set_interface"), ifname);
554                         _SysClose(fd);
555                 }
556         }
557 }
558

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