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

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