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

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