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

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