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

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