Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / clients / net-snk / app / netlib / dhcp.c
1 /******************************************************************************
2  * Copyright (c) 2004, 2008 IBM Corporation
3  * All rights reserved.
4  * This program and the accompanying materials
5  * are made available under the terms of the BSD License
6  * which accompanies this distribution, and is available at
7  * http://www.opensource.org/licenses/bsd-license.php
8  *
9  * Contributors:
10  *     IBM Corporation - initial implementation
11  *****************************************************************************/
12
13
14 /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ALGORITHMS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
15
16 /** \file dhcp.c <pre>
17  * **************** State-transition diagram for DHCP client  *************
18  *
19  *   +---------+                  Note: DHCP-server msg / DHCP-client msg
20  *   |  INIT   |
21  *   +---------+
22  *        |
23  *        |  - / Discover
24  *        V
25  *   +---------+
26  *   | SELECT  |                     Timeout
27  *   +---------+                        |
28  *        |                             |
29  *        |  Offer / Request            |
30  *        |                             |
31  *        V                             V
32  *   +---------+     NACK / -      ***********
33  *   | REQUEST | ----------------> *  FAULT  *
34  *   +---------+                   ***********
35  *        |
36  *        |          ACK / -       ***********
37  *        +----------------------> * SUCCESS *
38  *                                 ***********
39  *
40  * ************************************************************************
41  * </pre> */
42
43
44 /*>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<<<*/
45
46 #include <dhcp.h>
47 #include <ethernet.h>
48 #include <ipv4.h>
49 #include <udp.h>
50 #include <dns.h>
51
52 #include <stdio.h>
53 #include <string.h>
54 #include <time.h>
55 #include <sys/socket.h>
56 #include <ctype.h>
57 #include <stdlib.h>
58
59 /* DHCP Message Types */
60 #define DHCPDISCOVER    1
61 #define DHCPOFFER       2
62 #define DHCPREQUEST     3
63 #define DHCPDECLINE     4
64 #define DHCPACK         5
65 #define DHCPNACK        6
66 #define DHCPRELEASE     7
67 #define DHCPINFORM      8
68
69 /* DHCP Option Codes */
70 #define DHCP_MASK              1
71 #define DHCP_ROUTER            3
72 #define DHCP_DNS               6
73 #define DHCP_REQUESTED_IP     50
74 #define DHCP_OVERLOAD         52
75 #define DHCP_MSG_TYPE         53
76 #define DHCP_SERVER_ID        54
77 #define DHCP_REQUEST_LIST     55
78 #define DHCP_TFTP_SERVER      66
79 #define DHCP_BOOTFILE         67
80 #define DHCP_CLIENT_ARCH      93
81 #define DHCP_ENDOPT         0xFF
82 #define DHCP_PADOPT         0x00
83
84 /* "file/sname" overload option values */
85 #define DHCP_OVERLOAD_FILE     1
86 #define DHCP_OVERLOAD_SNAME    2
87 #define DHCP_OVERLOAD_BOTH     3
88
89 /* DHCP states codes */
90 #define DHCP_STATE_SELECT      1
91 #define DHCP_STATE_REQUEST     2
92 #define DHCP_STATE_SUCCESS     3
93 #define DHCP_STATE_FAULT       4
94
95 /* DHCP Client Architecture */
96 #ifndef DHCPARCH
97 #define USE_DHCPARCH 0
98 #define DHCPARCH 0
99 #else
100 #define USE_DHCPARCH 1
101 #endif
102
103 static uint8_t dhcp_magic[] = {0x63, 0x82, 0x53, 0x63};
104 /**< DHCP_magic is a cookie, that identifies DHCP options (see RFC 2132) */
105
106 /** \struct dhcp_options_t
107  *  This structure is used to fill options in DHCP-msg during transmitting
108  *  or to retrieve options from DHCP-msg during receiving.
109  *  <p>
110  *  If flag[i] == TRUE then field for i-th option retains valid value and
111  *  information from this field may retrived (in case of receiving) or will
112  *  be transmitted (in case of transmitting).
113  *  
114  */
115 typedef struct {
116         uint8_t    flag[256];         /**< Show if corresponding opt. is valid */
117         uint8_t    request_list[256]; /**< o.55 If i-th member is TRUE, then i-th  
118                                           option will be requested from server */
119         uint32_t   server_ID;         /**< o.54 Identifies DHCP-server         */
120         uint32_t   requested_IP;      /**< o.50 Must be filled in DHCP-Request */
121         uint32_t   dns_IP;            /**< o. 6 DNS IP                         */
122         uint32_t   router_IP;         /**< o. 3 Router IP                      */
123         uint32_t   subnet_mask;       /**< o. 1 Subnet mask                    */
124         uint8_t    msg_type;          /**< o.53 DHCP-message type              */
125         uint8_t    overload;          /**< o.52 Overload sname/file fields     */
126         int8_t     tftp_server[256];  /**< o.66 TFTP server name               */
127         int8_t     bootfile[256];     /**< o.67 Boot file name                 */
128         uint16_t   client_arch;       /**< o.93 Client architecture type       */
129 } dhcp_options_t;
130
131 /** Stores state of DHCP-client (refer to State-transition diagram) */
132 static uint8_t dhcp_state;
133
134
135 /*>>>>>>>>>>>>>>>>>>>>>>>>>>>> PROTOTYPES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
136
137 static int32_t
138 dhcp_attempt(int fd);
139
140 static int32_t
141 dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct);
142
143 static int32_t
144 dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len,
145                     dhcp_options_t * opt_struct);
146
147 static int8_t
148 dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len,
149                    uint8_t src_options[], uint32_t src_len);
150
151 static int8_t
152 dhcp_find_option(uint8_t options[], uint32_t len,
153                  uint8_t op_code, uint32_t * op_offset);
154
155 static void
156 dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len,
157                    uint8_t * new_option);
158
159 static void
160 dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len,
161                     uint32_t dst_offset, uint8_t * new_option);
162
163 static void
164 dhcp_send_discover(int fd);
165
166 static void
167 dhcp_send_request(int fd);
168
169 static uint8_t
170 strtoip(int8_t * str, uint32_t * ip);
171
172
173 /*>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<<*/
174
175 static uint8_t  ether_packet[ETH_MTU_SIZE];
176 static uint32_t dhcp_own_ip        = 0;
177 static uint32_t dhcp_server_ip     = 0;
178 static uint32_t dhcp_siaddr_ip     = 0;
179 static int8_t   dhcp_filename[256];
180 static int8_t   dhcp_tftp_name[256];
181
182 static char   * response_buffer;
183
184 /*>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<*/
185
186 int32_t
187 dhcpv4(char *ret_buffer, filename_ip_t * fn_ip) {
188
189         uint32_t dhcp_tftp_ip     = 0;
190         int fd = fn_ip->fd;
191
192         strcpy((char *) dhcp_filename, "");
193         strcpy((char *) dhcp_tftp_name, "");
194
195         response_buffer = ret_buffer;
196
197         if (dhcp_attempt(fd) == 0)
198                 return -1;
199
200         if (fn_ip->own_ip) {
201                 dhcp_own_ip = fn_ip->own_ip;
202         }
203         if (fn_ip->server_ip) {
204                 dhcp_siaddr_ip = fn_ip->server_ip;
205         }
206         if(fn_ip->filename[0] != 0) {
207                 strcpy((char *) dhcp_filename, (char *) fn_ip->filename);
208         }
209
210         // TFTP SERVER
211         if (!strlen((char *) dhcp_tftp_name)) {
212                 if (!dhcp_siaddr_ip) {
213                         // ERROR: TFTP name is not presented
214                         return -3;
215                 }
216
217                 // take TFTP-ip from siaddr field
218                 dhcp_tftp_ip = dhcp_siaddr_ip;
219         }
220         else {
221                 // TFTP server defined by its name
222                 if (!strtoip(dhcp_tftp_name, &(dhcp_tftp_ip))) {
223                         if (!dns_get_ip(fd, dhcp_tftp_name, (uint8_t *)&(dhcp_tftp_ip), 4)) {
224                                 // DNS error - can't obtain TFTP-server name  
225                                 // Use TFTP-ip from siaddr field, if presented
226                                 if (dhcp_siaddr_ip) {
227                                         dhcp_tftp_ip = dhcp_siaddr_ip;
228                                 }
229                                 else {
230                                         // ERROR: Can't obtain TFTP server IP
231                                         return -4;
232                                 }
233                         }
234                 }
235         }
236
237         // Store configuration info into filename_ip strucutre
238         fn_ip -> own_ip = dhcp_own_ip;
239         fn_ip -> server_ip = dhcp_tftp_ip;
240         strcpy((char *) fn_ip -> filename, (char *) dhcp_filename);
241
242         return 0;
243 }
244
245 /**
246  * DHCP: Tries o obtain DHCP parameters, refer to state-transition diagram
247  */
248 static int32_t
249 dhcp_attempt(int fd) {
250         int sec;
251
252         // Send DISCOVER message and switch DHCP-client to SELECT state
253         dhcp_send_discover(fd);
254
255         dhcp_state = DHCP_STATE_SELECT;
256
257         // setting up a timer with a timeout of two seconds
258         for (sec = 0; sec < 2; sec++) {
259                 set_timer(TICKS_SEC);
260                 do {
261                         receive_ether(fd);
262
263                         // Wait until client will switch to Final state or Timeout occurs
264                         switch (dhcp_state) {
265                         case DHCP_STATE_SUCCESS :
266                                 return 1;
267                         case DHCP_STATE_FAULT :
268                                 return 0;
269                         }
270                 } while (get_timer() > 0);
271         }
272
273         // timeout 
274         return 0;
275 }
276
277 /**
278  * DHCP: Supplements DHCP-message with options stored in structure.
279  *       For more information about option coding see dhcp_options_t.
280  *
281  * @param  opt_field     Points to the "vend" field of DHCP-message  
282  *                       (destination)
283  * @param  opt_struct    this structure stores info about the options which
284  *                       will be added to DHCP-message (source)
285  * @return               TRUE - options packed;
286  *                       FALSE - error condition occurs.
287  * @see                  dhcp_options_t
288  */
289 static int32_t
290 dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct) {
291         uint8_t * options = opt_field;
292         uint16_t i, sum; // used to define is any options set
293
294         // magic
295         memcpy(options, dhcp_magic, 4);
296         options += 4;
297
298         // fill message type
299         switch (opt_struct -> msg_type) {
300         case DHCPDISCOVER :
301         case DHCPREQUEST :
302         case DHCPDECLINE :
303         case DHCPINFORM :
304         case DHCPRELEASE :
305                 options[0] = DHCP_MSG_TYPE;
306                 options[1] = 1;
307                 options[2] = opt_struct -> msg_type;
308                 options += 3;
309                 break;
310         default :
311                 return 0; // Unsupported DHCP-message
312         }
313
314         if (opt_struct -> overload) {
315                 options[0] = DHCP_OVERLOAD;
316                 options[1] = 0x01;
317                 options[2] = opt_struct -> overload;
318                 options +=3;
319         }
320
321         if (opt_struct -> flag[DHCP_REQUESTED_IP]) {
322                 options[0] = DHCP_REQUESTED_IP;
323                 options[1] = 0x04;
324                 * (uint32_t *) (options + 2) = htonl (opt_struct -> requested_IP);
325                 options +=6;
326         }
327
328         if (opt_struct -> flag[DHCP_SERVER_ID]) {
329                 options[0] = DHCP_SERVER_ID;
330                 options[1] = 0x04;
331                 * (uint32_t *) (options + 2) = htonl (opt_struct -> server_ID);
332                 options +=6;
333         }
334
335         sum = 0;
336         for (i = 0; i < 256; i++)
337                 sum += opt_struct -> request_list[i];
338
339         if (sum) {
340                 options[0] = DHCP_REQUEST_LIST;
341                 options[1] = sum;
342                 options += 2;
343                 for (i = 0; i < 256; i++) {
344                         if (opt_struct -> request_list[i]) {
345                                 options[0] = i; options++;
346                         }
347                 }
348         }
349
350         if (opt_struct -> flag[DHCP_TFTP_SERVER]) {
351                 options[0] = DHCP_TFTP_SERVER;
352                 options[1] = strlen((char *) opt_struct -> tftp_server) + 1;
353                 memcpy(options + 2, opt_struct -> tftp_server, options[1]);
354                 options += options[1] + 2;
355         }
356
357         if (opt_struct -> flag[DHCP_BOOTFILE]) {
358                 options[0] = DHCP_BOOTFILE;
359                 options[1] = strlen((char *) opt_struct -> bootfile) + 1;
360                 memcpy(options + 2, opt_struct -> bootfile, options[1]);
361                 options += options[1] + 2;
362         }
363
364         if (opt_struct -> flag[DHCP_CLIENT_ARCH]) {
365                 options[0] = DHCP_CLIENT_ARCH;
366                 options[1] = 2;
367                 options[2] = (DHCPARCH >> 8);
368                 options[3] = DHCPARCH & 0xff;
369                 options += 4;
370         }
371
372         // end options
373         options[0] = 0xFF;
374         options++;
375
376         return 1;
377 }
378
379 /**
380  * DHCP: Extracts encoded options from DHCP-message into the structure.
381  *       For more information about option coding see dhcp_options_t.
382  *
383  * @param  opt_field     Points to the "options" field of DHCP-message  
384  *                       (source).
385  * @param  opt_len       Length of "options" field.
386  * @param  opt_struct    this structure stores info about the options which
387  *                       was extracted from DHCP-message (destination).
388  * @return               TRUE - options extracted;
389  *                       FALSE - error condition occurs.
390  * @see                  dhcp_options_t
391  */
392 static int32_t
393 dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len,
394                     dhcp_options_t * opt_struct) {
395         int32_t offset = 0;
396
397         memset(opt_struct, 0, sizeof(dhcp_options_t));
398
399         // magic
400         if (memcmp(opt_field, dhcp_magic, 4)) {
401                 return 0;
402         }
403
404         offset += 4;
405         while (offset < opt_len) {
406                 opt_struct -> flag[opt_field[offset]] = 1;
407                 switch(opt_field[offset]) {
408                 case DHCP_OVERLOAD :
409                         opt_struct -> overload = opt_field[offset + 2];
410                         offset += 2 + opt_field[offset + 1]; 
411                         break;
412
413                 case DHCP_REQUESTED_IP :
414                         opt_struct -> requested_IP = htonl(* (uint32_t *) (opt_field + offset + 2));
415                         offset += 2 + opt_field[offset + 1]; 
416                         break;
417
418                 case DHCP_MASK :
419                         opt_struct -> flag[DHCP_MASK] = 1;
420                         opt_struct -> subnet_mask = htonl(* (uint32_t *) (opt_field + offset + 2));
421                         offset += 2 + opt_field[offset + 1]; 
422                         break;
423
424                 case DHCP_DNS :
425                         opt_struct -> flag[DHCP_DNS] = 1;
426                         opt_struct -> dns_IP = htonl(* (uint32_t *) (opt_field + offset + 2));
427                         offset += 2 + opt_field[offset + 1]; 
428                         break;
429
430                 case DHCP_ROUTER :
431                         opt_struct -> flag[DHCP_ROUTER] = 1;
432                         opt_struct -> router_IP = htonl(* (uint32_t *) (opt_field + offset + 2));
433                         offset += 2 + opt_field[offset + 1]; 
434                         break;
435
436                 case DHCP_MSG_TYPE :
437                         if ((opt_field[offset + 2] > 0) && (opt_field[offset + 2] < 9))
438                                 opt_struct -> msg_type = opt_field[offset + 2];
439                         else
440                                 return 0;
441                         offset += 2 + opt_field[offset + 1];
442                         break;
443
444                 case DHCP_SERVER_ID :
445                         opt_struct -> server_ID = htonl(* (uint32_t *) (opt_field + offset + 2));
446                         offset += 2 + opt_field[offset + 1];
447                         break;
448
449                 case DHCP_TFTP_SERVER   :
450                         memcpy(opt_struct -> tftp_server, opt_field + offset + 2, opt_field[offset + 1]);
451                         (opt_struct -> tftp_server)[opt_field[offset + 1]] = 0;
452                         offset += 2 + opt_field[offset + 1];
453                         break;
454
455                 case DHCP_BOOTFILE :
456                         memcpy(opt_struct ->  bootfile, opt_field + offset + 2, opt_field[offset + 1]);
457                         (opt_struct -> bootfile)[opt_field[offset + 1]] = 0;
458                         offset += 2 + opt_field[offset + 1];
459                         break;
460
461                 case DHCP_CLIENT_ARCH :
462                         opt_struct -> client_arch = ((opt_field[offset + 2] << 8) & 0xFF00) | (opt_field[offset + 3] & 0xFF);
463                         offset += 4;
464                         break;
465
466                 case DHCP_PADOPT :
467                         offset++;
468                         break;
469
470                 case DHCP_ENDOPT :  // End of options
471                         return 1;
472
473                 default :
474                         offset += 2 + opt_field[offset + 1]; // Unsupported opt. - do nothing
475                 }
476         }
477         if (offset == opt_len)
478                 return 1; // options finished without 0xFF
479
480         return 0;
481 }
482
483 /**
484  * DHCP: Appends information from source "options" into dest "options".
485  *       This function is used to support "file/sname" overloading.
486  *
487  * @param  dst_options   destanation "options" field
488  * @param  dst_len       size of dst_options (modified by this function)
489  * @param  src_options   source "options" field
490  * @param  src_len       size of src_options
491  * @return               TRUE - options merged;
492  *                       FALSE - error condition occurs.
493  */
494 static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len,
495                                  uint8_t src_options[], uint32_t src_len) {
496         int32_t dst_offset, src_offset = 0;
497
498         // remove ENDOPT if presented
499         if (dhcp_find_option(dst_options, * dst_len, DHCP_ENDOPT, (uint32_t *) &dst_offset))
500                 * dst_len = dst_offset;
501
502         while (src_offset < src_len) {
503                 switch(src_options[src_offset]) {
504                 case DHCP_PADOPT:
505                         src_offset++;
506                         break;
507                 case DHCP_ENDOPT:
508                         return 1;
509                 default:
510                         if (dhcp_find_option(dst_options, * dst_len,
511                                              src_options[src_offset],
512                                              (uint32_t *) &dst_offset)) {
513                                 dhcp_combine_option(dst_options, dst_len,
514                                                     dst_offset,
515                                                     (uint8_t *) src_options +
516                                                     src_offset);
517                         }
518                         else {
519                                 dhcp_append_option(dst_options, dst_len, src_options + src_offset);
520                         }
521                         src_offset += 2 + src_options[src_offset + 1];
522                 }
523         }
524
525         if (src_offset == src_len) 
526                 return 1;
527         return 0;
528 }
529
530 /**
531  * DHCP: Finds given occurrence of the option with the given code (op_code)
532  *       in "options" field of DHCP-message.
533  *
534  * @param  options       "options" field of DHCP-message
535  * @param  len           length of the "options" field
536  * @param  op_code       code of the option to find
537  * @param  op_offset     SUCCESS - offset to an option occurrence;
538  *                       FAULT - offset is set to zero.
539  * @return               TRUE - option was find;
540  *                       FALSE - option wasn't find.
541  */
542 static int8_t dhcp_find_option(uint8_t options[], uint32_t len,
543                                uint8_t op_code, uint32_t * op_offset) {
544         uint32_t srch_offset = 0;
545         * op_offset = 0;
546
547         while (srch_offset < len) {
548                 if (options[srch_offset] == op_code) {
549                         * op_offset = srch_offset;
550                         return 1;
551                 }
552                 if (options[srch_offset] == DHCP_ENDOPT)
553                         return 0;
554
555                 if (options[srch_offset] == DHCP_PADOPT)
556                         srch_offset++;
557                 else
558                         srch_offset += 2 + options[srch_offset + 1];
559         }
560         return 0;
561 }
562
563 /**
564  * DHCP: Appends new option from one list (src) into the tail
565  *       of another option list (dst)
566  *
567  * @param  dst_options   "options" field of DHCP-message
568  * @param  dst_len       length of the "options" field (modified)
569  * @param  new_option    points to an option in another list (src)
570  */
571 static void
572 dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len,
573                    uint8_t * new_option) {
574         memcpy(dst_options + ( * dst_len), new_option, 2 + (* (new_option + 1)));
575         * dst_len += 2 + *(new_option + 1);
576 }
577
578 /**
579  * DHCP: This function is used when options with the same code are
580  *       presented in both merged lists. In this case information
581  *       about the option from one list (src) is combined (complemented)
582  *       with information about the option in another list (dst).
583  *
584  * @param  dst_options  "options" field of DHCP-message
585  * @param  dst_len       length of the "options" field (modified)
586  * @param  dst_offset    offset of the option from beginning of the list
587  * @param  new_option    points to an option in another list (src)
588  */
589 static void
590 dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len,
591                     uint32_t dst_offset, uint8_t * new_option) {
592
593         uint8_t tmp_buffer[1024]; // use to provide safe memcpy
594         uint32_t tail_len;
595
596         // move all subsequent options (allocate size for additional info)
597         tail_len = (* dst_len) - dst_offset - 2 - dst_options[dst_offset + 1];
598
599         memcpy(tmp_buffer, dst_options + (* dst_len) - tail_len, tail_len);
600         memcpy(dst_options + (* dst_len) - tail_len + (* (new_option + 1)),
601                tmp_buffer, tail_len);
602
603         // add new_content to option
604         memcpy(dst_options + (* dst_len) - tail_len, new_option + 2,
605                * (new_option + 1));
606         dst_options[dst_offset + 1] += * (new_option + 1);
607
608         // correct dst_len
609         * dst_len += * (new_option + 1);
610 }
611
612 /**
613  * DHCP: Sends DHCP-Discover message. Looks for DHCP servers.
614  */
615 static void
616 dhcp_send_discover(int fd) {
617         uint32_t packetsize = sizeof(struct iphdr) +
618                               sizeof(struct udphdr) + sizeof(struct btphdr);
619         struct btphdr *btph;
620         dhcp_options_t opt;
621
622         memset(ether_packet, 0, packetsize);
623
624         btph = (struct btphdr *) (&ether_packet[
625                sizeof(struct iphdr) + sizeof(struct udphdr)]);
626
627         btph -> op = 1;
628         btph -> htype = 1;
629         btph -> hlen = 6;
630         memcpy(btph -> chaddr, get_mac_address(), 6);
631
632         memset(&opt, 0, sizeof(dhcp_options_t));
633
634         opt.msg_type = DHCPDISCOVER;
635
636         opt.request_list[DHCP_MASK] = 1;
637         opt.request_list[DHCP_DNS] = 1;
638         opt.request_list[DHCP_ROUTER] = 1;
639         opt.request_list[DHCP_TFTP_SERVER] = 1;
640         opt.request_list[DHCP_BOOTFILE] = 1;
641         opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
642
643         dhcp_encode_options(btph -> vend, &opt);
644
645         fill_udphdr(&ether_packet[sizeof(struct iphdr)],
646                     sizeof(struct btphdr) + sizeof(struct udphdr),
647                     UDPPORT_BOOTPC, UDPPORT_BOOTPS);
648         fill_iphdr(ether_packet, sizeof(struct btphdr) +
649                    sizeof(struct udphdr) + sizeof(struct iphdr),
650                    IPTYPE_UDP, dhcp_own_ip, 0xFFFFFFFF);
651
652         send_ipv4(fd, ether_packet, packetsize);
653 }
654
655 /**
656  * DHCP: Sends DHCP-Request message. Asks for acknowledgment to occupy IP.
657  */
658 static void
659 dhcp_send_request(int fd) {
660         uint32_t packetsize = sizeof(struct iphdr) +
661                               sizeof(struct udphdr) + sizeof(struct btphdr);
662         struct btphdr *btph;
663         dhcp_options_t opt;
664
665         memset(ether_packet, 0, packetsize);
666
667         btph = (struct btphdr *) (&ether_packet[
668                sizeof(struct iphdr) + sizeof(struct udphdr)]);
669
670         btph -> op = 1;
671         btph -> htype = 1;
672         btph -> hlen = 6;
673         memcpy(btph -> chaddr, get_mac_address(), 6);
674
675         memset(&opt, 0, sizeof(dhcp_options_t));
676
677         opt.msg_type = DHCPREQUEST;
678         memcpy(&(opt.requested_IP), &dhcp_own_ip, 4);
679         opt.flag[DHCP_REQUESTED_IP] = 1;
680         memcpy(&(opt.server_ID), &dhcp_server_ip, 4);
681         opt.flag[DHCP_SERVER_ID] = 1;
682
683         opt.request_list[DHCP_MASK] = 1;
684         opt.request_list[DHCP_DNS] = 1;
685         opt.request_list[DHCP_ROUTER] = 1;
686         opt.request_list[DHCP_TFTP_SERVER] = 1;
687         opt.request_list[DHCP_BOOTFILE] = 1;
688         opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
689         opt.flag[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
690
691         dhcp_encode_options(btph -> vend, &opt);
692
693         fill_udphdr(&ether_packet[sizeof(struct iphdr)],
694                     sizeof(struct btphdr) + sizeof(struct udphdr),
695                     UDPPORT_BOOTPC, UDPPORT_BOOTPS);
696         fill_iphdr(ether_packet, sizeof(struct btphdr) +
697                    sizeof(struct udphdr) + sizeof(struct iphdr),
698                    IPTYPE_UDP, 0, 0xFFFFFFFF);
699
700         send_ipv4(fd, ether_packet, packetsize);
701 }
702
703
704 /**
705  * DHCP: Sends DHCP-Release message. Releases occupied IP.
706  */
707 void dhcp_send_release(int fd) {
708         uint32_t packetsize = sizeof(struct iphdr) +
709                               sizeof(struct udphdr) + sizeof(struct btphdr);
710         struct btphdr *btph;
711         dhcp_options_t opt;
712
713         btph = (struct btphdr *) (&ether_packet[
714                sizeof(struct iphdr) + sizeof(struct udphdr)]);
715
716         memset(ether_packet, 0, packetsize);
717
718         btph -> op = 1;
719         btph -> htype = 1;
720         btph -> hlen = 6;
721         strcpy((char *) btph -> file, "");
722         memcpy(btph -> chaddr, get_mac_address(), 6);
723         btph -> ciaddr = htonl(dhcp_own_ip);
724
725         memset(&opt, 0, sizeof(dhcp_options_t));
726
727         opt.msg_type = DHCPRELEASE;
728         opt.server_ID = dhcp_server_ip;
729         opt.flag[DHCP_SERVER_ID] = 1;
730
731         dhcp_encode_options(btph -> vend, &opt);
732
733         fill_udphdr(&ether_packet[sizeof(struct iphdr)], 
734                     sizeof(struct btphdr) + sizeof(struct udphdr),
735                     UDPPORT_BOOTPC, UDPPORT_BOOTPS);
736         fill_iphdr(ether_packet, sizeof(struct btphdr) +
737                    sizeof(struct udphdr) + sizeof(struct iphdr), IPTYPE_UDP,
738                    dhcp_own_ip, dhcp_server_ip);
739
740         send_ipv4(fd, ether_packet, packetsize);
741 }
742
743 /**
744  * DHCP: Handles DHCP-messages according to Receive-handle diagram.
745  *       Changes the state of DHCP-client.
746  *
747  * @param  fd         socket descriptor
748  * @param  packet     BootP/DHCP-packet to be handled
749  * @param  packetsize length of the packet
750  * @return            ZERO - packet handled successfully;
751  *                    NON ZERO - packet was not handled (e.g. bad format)
752  * @see               receive_ether
753  * @see               btphdr
754  */
755
756 int8_t
757 handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) {
758         struct btphdr * btph;
759         struct iphdr * iph;
760         dhcp_options_t opt;
761
762         memset(&opt, 0, sizeof(dhcp_options_t));  
763         btph = (struct btphdr *) packet;
764         iph = (struct iphdr *) packet - sizeof(struct udphdr) -
765               sizeof(struct iphdr);
766         if (btph -> op != 2)
767                 return -1; // it is not Boot Reply
768
769         if (memcmp(btph -> vend, dhcp_magic, 4)) {
770                 // It is BootP - RFC 951
771                 dhcp_own_ip    = htonl(btph -> yiaddr);
772                 dhcp_siaddr_ip = htonl(btph -> siaddr);
773                 dhcp_server_ip = htonl(iph -> ip_src);
774
775                 if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) {
776                         strncpy((char *) dhcp_tftp_name, (char *) btph -> sname,
777                                 sizeof(btph -> sname));
778                         dhcp_tftp_name[sizeof(btph -> sname)] = 0;
779                 }
780
781                 if (strlen((char *) btph -> file)) {
782                         strncpy((char *) dhcp_filename, (char *) btph -> file, sizeof(btph -> file));
783                         dhcp_filename[sizeof(btph -> file)] = 0;
784                 }
785
786                 dhcp_state = DHCP_STATE_SUCCESS;
787                 return 0;
788         }
789
790
791         // decode options  
792         if (!dhcp_decode_options(btph -> vend, packetsize -
793                                  sizeof(struct btphdr) + sizeof(btph -> vend),
794                                  &opt)) {
795                 return -1;  // can't decode options
796         }
797
798         if (opt.overload) {
799                 int16_t decode_res = 0;
800                 uint8_t options[1024]; // buffer for merged options
801                 uint32_t opt_len;
802
803                 // move 1-st part of options from vend field into buffer
804                 opt_len = packetsize - sizeof(struct btphdr) +
805                           sizeof(btph -> vend) - 4;
806                 memcpy(options, btph -> vend, opt_len + 4);
807
808                 // add other parts
809                 switch (opt.overload) {
810                 case DHCP_OVERLOAD_FILE:
811                         decode_res = dhcp_merge_options(options + 4, &opt_len,
812                                                         btph -> file,
813                                                         sizeof(btph -> file));
814                         break;
815                 case DHCP_OVERLOAD_SNAME:
816                         decode_res = dhcp_merge_options(options + 4, &opt_len,
817                                                         btph -> sname,
818                                                         sizeof(btph -> sname));
819                         break;
820                 case DHCP_OVERLOAD_BOTH:
821                         decode_res = dhcp_merge_options(options + 4, &opt_len,
822                                                         btph -> file,
823                                                         sizeof(btph -> file));
824                         if (!decode_res)
825                                 break;
826                         decode_res = dhcp_merge_options(options + 4, &opt_len,
827                                                         btph -> sname,
828                                                         sizeof(btph -> sname));
829                         break;
830                 }
831
832                 if (!decode_res)
833                         return -1; // bad options in sname/file fields
834
835                 // decode merged options
836                 if (!dhcp_decode_options(options, opt_len + 4, &opt)) {
837                         return -1; // can't decode options
838                 }
839         }
840
841         if (!opt.msg_type) {
842                 // It is BootP with Extensions - RFC 1497
843                 // retrieve conf. settings from BootP - reply
844                 dhcp_own_ip = htonl(btph -> yiaddr);
845                 dhcp_siaddr_ip = htonl(btph -> siaddr);
846                 if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) {
847                         strncpy((char *) dhcp_tftp_name, (char *) btph -> sname, sizeof(btph -> sname));
848                         dhcp_tftp_name[sizeof(btph -> sname)] = 0;
849                 }
850
851                 if (strlen((char *) btph -> file)) {
852                         strncpy((char *) dhcp_filename, (char *) btph -> file, sizeof(btph -> file));
853                         dhcp_filename[sizeof(btph -> file)] = 0;
854                 }
855
856                 // retrieve DHCP-server IP from IP-header
857                 dhcp_server_ip = iph -> htonl(ip_src);
858
859                 dhcp_state = DHCP_STATE_SUCCESS;
860         }
861         else {
862                 // It is DHCP - RFC 2131 & RFC 2132
863                 // opt contains parameters from server
864                 switch (dhcp_state) {
865                 case DHCP_STATE_SELECT :
866                         if (opt.msg_type == DHCPOFFER) {
867                                 dhcp_own_ip = htonl(btph -> yiaddr);
868                                 dhcp_server_ip = opt.server_ID;
869                                 dhcp_send_request(fd);
870                                 dhcp_state = DHCP_STATE_REQUEST;
871                         }
872                         return 0;
873                 case DHCP_STATE_REQUEST :
874                         switch (opt.msg_type) {
875                         case DHCPNACK :
876                                 dhcp_own_ip = 0;
877                                 dhcp_server_ip = 0;
878                                 dhcp_state = DHCP_STATE_FAULT;
879                                 break;
880                         case DHCPACK :
881                                 dhcp_own_ip = htonl(btph -> yiaddr);
882                                 dhcp_server_ip = opt.server_ID;
883                                 dhcp_siaddr_ip = htonl(btph -> siaddr);
884                                 if (opt.flag[DHCP_TFTP_SERVER]) {
885                                         strcpy((char *) dhcp_tftp_name, (char *) opt.tftp_server);
886                                 }
887                                 else {
888                                         strcpy((char *) dhcp_tftp_name, "");
889                                         if ((opt.overload != DHCP_OVERLOAD_SNAME &&
890                                              opt.overload != DHCP_OVERLOAD_BOTH) &&
891                                              !dhcp_siaddr_ip) {
892                                                 strncpy((char *) dhcp_tftp_name,
893                                                         (char *) btph->sname,
894                                                         sizeof(btph -> sname));
895                                                 dhcp_tftp_name[sizeof(btph->sname)] = 0;
896                                         }
897                                 }
898
899                                 if (opt.flag[DHCP_BOOTFILE]) {
900                                         strcpy((char *) dhcp_filename, (char *) opt.bootfile);
901                                 }
902                                 else {
903                                         strcpy((char *) dhcp_filename, "");
904                                         if (opt.overload != DHCP_OVERLOAD_FILE &&
905                                                 opt.overload != DHCP_OVERLOAD_BOTH && 
906                                                 strlen((char *) btph -> file)) {
907                                                 strncpy((char *) dhcp_filename,
908                                                         (char *) btph->file,
909                                                         sizeof(btph->file));
910                                                 dhcp_filename[sizeof(btph -> file)] = 0;
911                                         }
912                                 }
913
914                                 dhcp_state = DHCP_STATE_SUCCESS;
915                                 break;
916                         default:
917                                 break; // Unused DHCP-message - do nothing
918                         }
919                         break;
920                 default :
921                         return -1; // Illegal DHCP-client state
922                 }
923         }
924
925         if (dhcp_state == DHCP_STATE_SUCCESS) {
926
927                 // initialize network entity with real own_ip
928                 // to be able to answer for foreign requests
929                 set_ipv4_address(dhcp_own_ip);
930
931                 if(response_buffer) {
932                         if(packetsize <= 1720)
933                                 memcpy(response_buffer, packet, packetsize);
934                         else
935                                 memcpy(response_buffer, packet, 1720);
936                 }
937
938                 /* Subnet mask */
939                 if (opt.flag[DHCP_MASK]) {
940                         /* Router */
941                         if (opt.flag[DHCP_ROUTER]) {
942                                 set_ipv4_router(opt.router_IP);
943                                 set_ipv4_netmask(opt.subnet_mask);
944                         }
945                 }
946
947                 /* DNS-server */
948                 if (opt.flag[DHCP_DNS]) {
949                         dns_init(opt.dns_IP, 0, 4);
950                 }
951         }
952
953         return 0;
954 }
955
956 /**
957  * DHCP: Converts "255.255.255.255" -> 32-bit long IP
958  *
959  * @param  str        string to be converted
960  * @param  ip         in case of SUCCESS - 32-bit long IP
961                       in case of FAULT - zero
962  * @return            TRUE - IP converted successfully;
963  *                    FALSE - error condition occurs (e.g. bad format)
964  */
965 static uint8_t
966 strtoip(int8_t * str, uint32_t * ip) {
967         int8_t ** ptr = &str;
968         int16_t i = 0, res, len;
969         char octet[256];
970
971         * ip = 0;
972
973         while (**ptr != 0) {
974                 if (i > 3 || !isdigit(**ptr))
975                         return 0;
976                 if (strstr((char *) * ptr, ".") != NULL) {
977                         len = (int16_t) ((int8_t *) strstr((char *) * ptr, ".") - 
978                               (int8_t *) (* ptr));
979                         strncpy(octet, (char *) * ptr, len); octet[len] = 0;
980                         * ptr += len;
981                 }
982                 else {
983                         strcpy(octet, (char *) * ptr);
984                         * ptr += strlen(octet);
985                 }
986                 res = strtol(octet, NULL, 10);
987                 if ((res > 255) || (res < 0))
988                         return 0;
989                 * ip = ((* ip) << 8) + res;
990                 i++;
991                 if (** ptr == '.')
992                         (*ptr)++;
993         }
994
995         if (i != 4)
996                 return 0;
997         return 1;
998 }