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

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