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