Merge remote branch 'origin/master' into dhcp-client
[ZeXOS.git] / kernel / core / net / dhcp.c
blob005537bdccfda500c2b37f9535c60030cfb3d5df
1 /*
2 * ZeX/OS
3 * Copyright (C) 2009 Martin 'povik' Poviser (martin.povik@gmail.com)
4 * Copyright (C) 2010 Tomas 'ZeXx86' Jedrzejek (zexx86@gmail.com)
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
21 #include <system.h>
22 #include <string.h>
23 #include <net/if.h>
24 #include <net/ip.h>
25 #include <net/dhcp.h>
26 #include <net/socket.h>
27 #include <net/hostname.h>
29 #define DHCP_REQUEST_MAX_LEN 4096
31 int dhcp_request_header_prepare (dhcp_request_t request, netif_t *netif,
32 char req_type)
34 request.bootp_header->op = req_type; /* Request type */
35 request.bootp_header->htype = 0x01; /* Connection type : Ethernet */ /* TODO */
36 request.bootp_header->hlen = 0x06; /* Hardware address length : 6 bytes */
37 request.bootp_header->hops = 0x00; /* Clients set to zero */
38 request.bootp_header->xid = 0x12345678; /* Random request ID */
39 request.bootp_header->secs = 0x0000; /* Is'nt needed in dhcp request */
40 request.bootp_header->flags = 0x0000; /* No flags */
42 request.bootp_header->ciaddr = NET_IPV4_TO_ADDR (0, 0, 0, 0);
43 request.bootp_header->yiaddr = NET_IPV4_TO_ADDR (0, 0, 0, 0);
44 request.bootp_header->siaddr = NET_IPV4_TO_ADDR (0, 0, 0, 0);
45 request.bootp_header->giaddr = NET_IPV4_TO_ADDR (0, 0, 0, 0);
47 memcpy (request.bootp_header->chaddr, netif->dev->dev_addr, 6);
49 request.bootp_header->sname[0] = 0;
50 request.bootp_header->file[0] = 0;
52 request.bootp_header->mag_cookie = DHCP_MAGIC_COOKIE;
54 return sizeof (dhcp_bootp_header_t);
57 int dhcp_request_option_add (dhcp_request_t request, char option_type, char *buffer,
58 int buffer_len, int pos)
60 request.buffer[pos] = option_type;
61 request.buffer[pos + 1] = buffer_len;
62 memcpy (request.buffer + pos + 2, buffer, buffer_len);
64 return pos + 2 + buffer_len;
67 int dhcp_config_if (netif_t *netif)
69 net_ipv4 server_addr = INADDR_BROADCAST;
70 sockaddr_in server_socket;
71 int sock = 0;
73 unsigned xid;
74 unsigned int pos;
75 dhcp_request_t request;
77 if (!netif)
78 return -1;
80 request.buffer = kmalloc (DHCP_REQUEST_MAX_LEN);
82 if (!request.buffer)
83 return -2;
85 char *buffer = kmalloc (1024);
87 if (!buffer) {
88 kfree (request.buffer);
89 return -2;
92 net_ipv4 config_gw = 0x00000000;
93 net_ipv4 config_ip = 0x00000000;
94 net_ipv4 config_dns = 0x00000000;
95 net_ipv4 config_mask = 0x00000000;
96 net_ipv4 config_dhcp = 0x00000000;
97 unsigned int config_lease_time = 0;
99 int ret = 0;
101 /* Create a socket */
102 if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
103 kprintf ("DHCP client -> Can't create socket.\n");
104 ret = DHCP_SOCKET_FAILED;
105 goto clear;
108 server_socket.sin_family = AF_INET;
109 server_socket.sin_port = htons (67);
110 memcpy (&(server_socket.sin_addr), &server_addr, sizeof (net_ipv4));
112 /* Let's connect to server */
113 if (connect (sock, (sockaddr *) &server_socket,
114 sizeof (server_socket)) == -1) {
115 kprintf ("DHCP client -> Connection can't be estabilished\n");
116 ret = DHCP_CONNECT_FAILED;
117 goto clear;
120 net_proto_udp_anycast (sock);
121 net_proto_udp_port (sock, htons (68));
123 pos = dhcp_request_header_prepare (request, netif,
124 DHCP_MESSAGE_TYPE_BOOTREQUEST);
125 xid = request.bootp_header->xid;
127 buffer[0] = DHCP_OPTION_VALUE_DHCPDISCOVER;
128 pos = dhcp_request_option_add (request, DHCP_OPTION_MESSAGE_TYPE,
129 buffer, 1, pos);
131 buffer[0] = 0x01;
132 memcpy (buffer + 1, netif->dev->dev_addr, 6);
133 pos = dhcp_request_option_add (request, DHCP_OPTION_CLIENT_IDENTIFIER,
134 buffer, 7, pos);
136 char *hostname = hostname_get ();
138 if (strlen (hostname) <= 128) {
139 unsigned host_len = strlen (hostname);
140 pos = dhcp_request_option_add (request, DHCP_OPTION_HOSTNAME,
141 buffer, host_len, pos);
144 buffer[0] = DHCP_OPTION_VALUE_SUBNET_MASK;
145 buffer[1] = DHCP_OPTION_VALUE_ROUTER;
146 buffer[2] = DHCP_OPTION_VALUE_DNS_SERVER;
147 buffer[3] = DHCP_OPTION_VALUE_DOMAIN_NAME;
148 pos = dhcp_request_option_add (request, DHCP_OPTION_PARAMETER_REQUEST_LIST,
149 buffer, 0x04, pos);
151 int recv_ret;
153 kprintf ("DHCP client -> sending request\n");
154 if (send (sock, request.buffer, pos, 0) < 0) {
155 kprintf ("DHCP client -> Can't send request.\n");
156 ret = DHCP_CANT_SEND_REQUEST;
157 goto clear;
159 kprintf ("DHCP client -> reuquest sended\n");
161 recv_dhcpoffer:
162 config_ip = 0x00000000;
163 config_gw = 0x00000000;
164 config_dns = 0x00000000;
165 config_dhcp = 0x00000000;
167 kprintf ("DHCP client -> receiving response\n");
168 if ((recv_ret = recv (sock, request.buffer, DHCP_REQUEST_MAX_LEN, 0)) < sizeof (dhcp_bootp_header_t)) {
169 kprintf ("DHCP client -> Can't receive response.\n");
170 ret = DHCP_CANT_RECV_RESPONSE;
171 goto clear;
173 kprintf ("DHCP client -> response received\n");
175 if (request.bootp_header->mag_cookie != DHCP_MAGIC_COOKIE) {
176 kprintf ("DHCP client -> bad magic (finded 0x%X - needed 0x%X\n",
177 request.bootp_header->mag_cookie, DHCP_MAGIC_COOKIE);
178 ret = DHCP_BAD_PACKET;
179 goto clear;
182 pos = sizeof (dhcp_bootp_header_t);
184 config_ip = request.bootp_header->yiaddr;
186 if (request.bootp_header->xid != xid) {
187 kprintf ("DHCP client -> bad xid");
188 goto recv_dhcpoffer;
191 char dhcpack = 0;
192 while (pos < recv_ret) {
193 unsigned int option_len;
195 if (request.buffer[pos] == DHCP_OPTION_PAD) {
196 pos += 1;
197 continue;
200 if (request.buffer[pos] == DHCP_OPTION_END)
201 break;
203 if (pos >= recv_ret - 2) {
204 ret = DHCP_BAD_PACKET;
205 goto clear;
208 option_len = request.buffer[pos + 1];
209 pos += 2;
211 if (pos + option_len >= recv_ret) {
212 ret = DHCP_BAD_PACKET;
213 goto clear;
215 switch (request.buffer[pos - 2]) {
216 case DHCP_OPTION_MESSAGE_TYPE:
217 if (!option_len == 1) {
218 ret = DHCP_BAD_PACKET;
219 goto clear;
221 switch (request.buffer[pos]) {
222 case DHCP_OPTION_VALUE_DHCPOFFER:
223 break;
224 case DHCP_OPTION_VALUE_DHCPACK:
225 dhcpack = 1;
226 break;
227 case DHCP_OPTION_VALUE_DHCPNAK:
228 ret = DHCP_SRV_DIDNT_UNDERSTAND;
229 goto clear;
230 break;
231 default:
232 kprintf ("DHCP client -> bad DHCP message type");
233 goto recv_dhcpoffer;
235 break;
236 case DHCP_OPTION_SUBNET_MASK:
237 if (option_len >= 4)
238 memcpy (&config_mask, request.buffer + pos, sizeof (net_ipv4));
239 break;
240 case DHCP_OPTION_ROUTER:
241 if (option_len >= 4)
242 memcpy (&config_gw, request.buffer + pos, sizeof (net_ipv4));
243 break;
244 case DHCP_OPTION_DNS_SERVER:
245 if (option_len >= 4)
246 memcpy (&config_dns, request.buffer + pos, sizeof (net_ipv4));
247 break;
248 case DHCP_OPTION_DHCP_SERVER:
249 if (option_len >= 4)
250 memcpy (&config_dhcp, request.buffer + pos, sizeof (net_ipv4));
251 break;
252 default:
253 break;
256 pos += option_len;
259 kprintf ("DHCP client -> response parsed\n");
261 if (dhcpack)
262 goto configure;
264 pos = dhcp_request_header_prepare (request, netif,
265 DHCP_MESSAGE_TYPE_BOOTREQUEST);
267 buffer[0] = DHCP_OPTION_VALUE_DHCPREQUEST;
268 pos = dhcp_request_option_add (request, DHCP_OPTION_MESSAGE_TYPE,
269 buffer, 1, pos);
271 memcpy (buffer, &config_ip, 4);
272 pos = dhcp_request_option_add (request, DHCP_OPTION_REQUESTED_IP,
273 buffer, 4, pos);
275 memcpy (buffer, &config_dhcp, 4);
276 pos = dhcp_request_option_add (request, DHCP_OPTION_DHCP_SERVER,
277 buffer, 4, pos);
279 kprintf ("DHCP client -> sending request\n");
280 if (send (sock, request.buffer, pos, 0) < 0) {
281 kprintf ("DHCP client -> Can't send request.\n");
282 ret = DHCP_CANT_SEND_REQUEST;
283 goto clear;
285 kprintf ("DHCP client -> request sended\n");
287 recv_dhcpack:
289 kprintf ("DHCP client -> receiving response\n");
290 if ((recv_ret = recv (sock, request.buffer, DHCP_REQUEST_MAX_LEN, 0)) < sizeof (dhcp_bootp_header_t)) {
291 kprintf ("DHCP client -> Can't receive response.\n");
292 ret = DHCP_CANT_RECV_RESPONSE;
293 goto clear;
295 kprintf ("DHCP client -> response received\n");
297 if (request.bootp_header->mag_cookie != DHCP_MAGIC_COOKIE) {
298 kprintf ("DHCP client -> bad magic (finded 0x%X - needed 0x%X\n",
299 request.bootp_header->mag_cookie, DHCP_MAGIC_COOKIE);
300 ret = DHCP_BAD_PACKET;
301 goto clear;
304 if (request.bootp_header->xid != xid) {
305 kprintf ("DHCP client -> bad xid");
306 goto recv_dhcpoffer;
309 pos = sizeof (dhcp_bootp_header_t);
311 while (pos < recv_ret) {
312 unsigned int option_len;
313 if (request.buffer[pos] == DHCP_OPTION_PAD) {
314 pos += 1;
315 continue;
318 if (request.buffer[pos] == DHCP_OPTION_END)
319 break;
321 if (pos >= recv_ret - 2) {
322 ret = DHCP_BAD_PACKET;
323 goto clear;
326 option_len = request.buffer[pos + 1];
327 pos += 2;
329 if (pos + option_len >= recv_ret) {
330 ret = DHCP_BAD_PACKET;
331 goto clear;
334 switch (request.buffer[pos - 2]) {
335 case DHCP_OPTION_MESSAGE_TYPE:
336 if (!option_len == 1) {
337 ret = DHCP_BAD_PACKET;
338 goto clear;
340 switch (request.buffer[pos]) {
341 case DHCP_OPTION_VALUE_DHCPOFFER:
342 ret = DHCP_BAD_PACKET;
343 goto clear;
344 break;
345 case DHCP_OPTION_VALUE_DHCPACK:
346 break;
347 case DHCP_OPTION_VALUE_DHCPNAK:
348 ret = DHCP_SRV_DIDNT_UNDERSTAND;
349 goto clear;
350 break;
351 default:
352 kprintf ("DHCP client -> bad DHCP message type");
353 goto recv_dhcpoffer;
355 break;
356 case DHCP_OPTION_SUBNET_MASK:
357 if (option_len >= 4)
358 memcpy (&config_mask, request.buffer + pos, sizeof (net_ipv4));
359 break;
360 case DHCP_OPTION_ROUTER:
361 if (option_len >= 4)
362 memcpy (&config_gw, request.buffer + pos, sizeof (net_ipv4));
363 break;
364 case DHCP_OPTION_DNS_SERVER:
365 if (option_len >= 4)
366 memcpy (&config_dns, request.buffer + pos, sizeof (net_ipv4));
367 break;
368 default:
369 break;
372 pos += option_len;
375 configure:
376 kprintf ("DHCP client -> configuring interface\n");
377 kprintf ("DHCP client -> my IP: ");
378 net_proto_ip_print (config_ip);
379 kprintf ("\nDHCP client -> router IP: ");
380 net_proto_ip_print (config_gw);
381 kprintf ("\nDHCP client -> DNS server IP: ");
382 net_proto_ip_print (config_dns);
383 kprintf ("\n");
385 dns_addr (config_dns);
386 netif_ip_addr (netif, config_ip, IF_CFG_TYPE_DHCP);
387 netif_gw_addr (netif, config_gw);
388 clear:
389 kfree (buffer);
390 kfree (request.buffer);
391 sclose (sock);
393 return ret;