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

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