1 /******************************************************************************
2  * Copyright (c) 2013 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 #include <string.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdint.h>
17 #include <sys/socket.h>
18 #include <time.h>
19 #include "ethernet.h"
20 #include "ipv6.h"
21 #include "udp.h"
22 #include "dhcpv6.h"
23 #include "tftp.h"
24 #include "dns.h"
25 
26 static uint8_t tid[3];
27 static uint32_t dhcpv6_state = -1;
28 static filename_ip_t *my_fn_ip;
29 
30 static struct ip6addr_list_entry all_dhcpv6_ll; /* All DHCPv6 servers address */
31 
32 void
dhcpv6_generate_transaction_id(void)33 dhcpv6_generate_transaction_id(void)
34 {
35 	/* As per RFC 3315 transaction IDs should be generated randomly */
36 	tid[0] = rand();
37 	tid[1] = rand();
38 	tid[2] = rand();
39 }
40 
41 static void
send_info_request(int fd)42 send_info_request(int fd)
43 {
44 	uint8_t ether_packet[ETH_MTU_SIZE];
45 	uint32_t payload_length;
46 	struct dhcp_message_header *dhcph;
47 
48 	memset(ether_packet, 0, ETH_MTU_SIZE);
49 
50 	/* Get an IPv6 packet */
51 	payload_length = sizeof(struct udphdr) + sizeof(struct dhcp_message_header);
52 	fill_ip6hdr (ether_packet + sizeof(struct ethhdr),
53 		     payload_length, IPTYPE_UDP,
54 		     get_ipv6_address(), &(all_dhcpv6_ll.addr));
55 	fill_udphdr ( ether_packet + sizeof(struct ethhdr) + sizeof(struct ip6hdr),
56 		      payload_length, DHCP_CLIENT_PORT, DHCP_SERVER_PORT);
57 	dhcph = (struct dhcp_message_header *) (ether_packet +
58 						sizeof(struct ethhdr) +
59 						sizeof(struct ip6hdr) +
60 						sizeof(struct udphdr));
61 
62 	/* Fill in DHCPv6 data */
63 	dhcph->type = DHCP_INFORMATION_REQUEST;
64 	memcpy( &(dhcph->transaction_id), &tid, 3);
65 	dhcph->option.client_id.code = DHCPV6_OPTION_CLIENTID;
66 	dhcph->option.client_id.length = 10;
67 	dhcph->option.client_id.duid_type = DUID_LL;
68 	dhcph->option.client_id.hardware_type = 1;
69 	memcpy( &(dhcph->option.client_id.mac),
70 		get_mac_address(), 6);
71 	dhcph->option.el_time.code = DHCPV6_OPTION_ELAPSED_TIME;
72 	dhcph->option.el_time.length = 2;
73 	dhcph->option.el_time.time = 0x190; /* 4000 ms */
74 	dhcph->option.option_request_option.code = DHCPV6_OPTION_ORO;
75 	dhcph->option.option_request_option.length = DHCPV6_OPTREQUEST_NUMOPTS * 2;
76 	dhcph->option.option_request_option.option_code[0] = DHCPV6_OPTION_DNS_SERVERS;
77 	dhcph->option.option_request_option.option_code[1] = DHCPV6_OPTION_DOMAIN_LIST;
78 	dhcph->option.option_request_option.option_code[2] = DHCPV6_OPTION_BOOT_URL;
79 
80 	send_ipv6(fd, ether_packet + sizeof(struct ethhdr),
81 		  sizeof(struct ip6hdr) + sizeof(struct udphdr)
82 		  + sizeof(struct dhcp_message_header));
83 }
84 
85 static int32_t
dhcpv6_attempt(int fd)86 dhcpv6_attempt(int fd)
87 {
88 	int sec;
89 
90 	// Send information request
91 	send_info_request(fd);
92 
93 	dhcpv6_state = DHCPV6_STATE_SELECT;
94 
95 	// setting up a timer with a timeout of two seconds
96 	for (sec = 0; sec < 2; sec++) {
97 		set_timer(TICKS_SEC);
98 		do {
99 			receive_ether(fd);
100 
101 			// Wait until client will switch to Final state or Timeout occurs
102 			switch (dhcpv6_state) {
103 			case DHCP_STATUSCODE_SUCCESS:
104 				return 1;
105 			case DHCP_STATUSCODE_UNSPECFAIL: //FIXME
106 				return 0;
107 			}
108 		} while (get_timer() > 0);
109 	}
110 
111 	// timeout
112 	return 0;
113 }
114 
115 int32_t
dhcpv6(char * ret_buffer,void * fn_ip)116 dhcpv6 ( char *ret_buffer, void *fn_ip)
117 {
118 	int fd;
119 
120 	all_dhcpv6_ll.addr.part.prefix = 0xff02000000000000ULL;
121 	all_dhcpv6_ll.addr.part.interface_id = 0x10002ULL;
122 
123 	my_fn_ip = (filename_ip_t *) fn_ip;
124 	fd = my_fn_ip->fd;
125 
126 	if( !dhcpv6_attempt(fd)) {
127 		return -1;
128 	}
129 
130 	return 0;
131 }
132 
dhcp6_process_options(uint8_t * option,int32_t option_length)133 static void dhcp6_process_options (uint8_t *option, int32_t option_length)
134 {
135 	struct dhcp_boot_url *option_boot_url;
136 	struct client_identifier *option_clientid;
137 	struct server_identifier *option_serverid;
138 	struct dhcp_dns *option_dns;
139 	struct dhcp_dns_list *option_dns_list;
140 	struct dhcp6_gen_option *option_gen;
141 	char buffer[256];
142 
143 	while (option_length > 0) {
144 		switch ((uint16_t) *(option+1)) {
145 		case DHCPV6_OPTION_CLIENTID:
146 			option_clientid = (struct client_identifier *) option;
147 			option = option +  option_clientid->length + 4;
148 			option_length = option_length - option_clientid->length - 4;
149 			break;
150 		case DHCPV6_OPTION_SERVERID:
151 			option_serverid = (struct server_identifier *) option;
152 			option = option +  option_serverid->length + 4;
153 			option_length = option_length - option_serverid->length - 4;
154 			break;
155 		case DHCPV6_OPTION_DNS_SERVERS:
156 			option_dns = (struct dhcp_dns *) option;
157 			option = option +  option_dns->length + 4;
158 			option_length = option_length - option_dns->length - 4;
159 			memcpy( &(my_fn_ip->dns_ip6),
160 				option_dns->p_ip6,
161 				IPV6_ADDR_LENGTH);
162 			dns_init(0, option_dns->p_ip6, 6);
163 			break;
164 		case DHCPV6_OPTION_DOMAIN_LIST:
165 			option_dns_list = (struct dhcp_dns_list *) option;
166 			option = option +  option_dns_list->length + 4;
167 			option_length = option_length - option_dns_list->length - 4;
168 			break;
169 		case DHCPV6_OPTION_BOOT_URL:
170 			option_boot_url = (struct dhcp_boot_url *) option;
171 			option = option +  option_boot_url->length + 4;
172 			option_length = option_length - option_boot_url->length - 4;
173 			strncpy((char *)buffer,
174 				(const char *)option_boot_url->url,
175 				(size_t)option_boot_url->length);
176 			buffer[option_boot_url->length] = 0;
177 			if (parse_tftp_args(buffer,
178 					    (char *)my_fn_ip->server_ip6.addr,
179 					    my_fn_ip->filename, my_fn_ip->fd,
180 					    option_boot_url->length) == -1)
181 				return;
182 			break;
183 		default:
184 			option_gen = (struct dhcp6_gen_option *) option;
185 			option = option + option_gen->length + 4;
186 			option_length = option_length - option_gen->length - 4;
187 		}
188 	}
189 }
190 
191 uint32_t
handle_dhcpv6(uint8_t * packet,int32_t packetsize)192 handle_dhcpv6(uint8_t * packet, int32_t packetsize)
193 {
194 
195 	uint8_t  *first_option;
196 	int32_t option_length;
197 	struct dhcp_message_reply *reply;
198 	reply = (struct dhcp_message_reply *) packet;
199 
200 	if (memcmp(reply->transaction_id, tid, 3))
201 		return -1;			/* Wrong transaction ID */
202 
203 	if (reply->type == 7)
204 		dhcpv6_state = DHCP_STATUSCODE_SUCCESS;
205 
206 	first_option =  packet + 4;
207 	option_length =  packet + packetsize - first_option;
208 	dhcp6_process_options(first_option, option_length);
209 
210 	return 0;
211 }
212