Added dhcpcl command; Some updates in kernel/core/net/dhcp.c;
[ZeXOS.git] / kernel / core / net / dhcp.c
blob859e66ec2071a455a4647568884a22bfa2211f47
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)
5 * Copyright (C) 2010 Martin 'povik' Poviser (martin.povik@gmail.com)
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
22 #include <system.h>
23 #include <string.h>
24 #include <net/if.h>
25 #include <net/ip.h>
26 #include <net/dhcp.h>
27 #include <net/socket.h>
28 #include <net/hostname.h>
30 #define DHCP_REQUEST_MAX_LEN 4096
32 int dhcp_request_header_prepare (dhcp_request_t request, netif_t *netif,
33 char req_type)
35 request.bootp_header->op = req_type; /* Request type */
36 request.bootp_header->htype = 0x01; /* Connection type : Ethernet */ /* TODO */
37 request.bootp_header->hlen = 0x06; /* Hardware address length : 6 bytes */
38 request.bootp_header->hops = 0x00; /* Clients set to zero */
39 request.bootp_header->xid = 0x12345678; /* Random request ID */
40 request.bootp_header->secs = 0x0000; /* Is'nt needed in dhcp request */
41 request.bootp_header->flags = 0x0000; /* No flags */
43 request.bootp_header->ciaddr = NET_IPV4_TO_ADDR (0, 0, 0, 0);
44 request.bootp_header->yiaddr = NET_IPV4_TO_ADDR (0, 0, 0, 0);
45 request.bootp_header->siaddr = NET_IPV4_TO_ADDR (0, 0, 0, 0);
46 request.bootp_header->giaddr = NET_IPV4_TO_ADDR (0, 0, 0, 0);
48 memcpy (request.bootp_header->chaddr, netif->dev->dev_addr, 6);
50 request.bootp_header->sname[0] = 0;
51 request.bootp_header->file[0] = 0;
53 request.bootp_header->mag_cookie = DHCP_MAGIC_COOKIE;
55 return sizeof (dhcp_bootp_header_t);
58 int dhcp_request_option_add (dhcp_request_t request, char option_type, char *buffer,
59 int buffer_len, int pos)
61 request.buffer[pos] = option_type;
62 request.buffer[pos + 1] = buffer_len;
63 memcpy (request.buffer + pos + 2, buffer, buffer_len);
65 return pos + 2 + buffer_len;
68 int dhcp_config_if (netif_t *netif)
70 net_ipv4 server_addr = INADDR_BROADCAST;
71 sockaddr_in server_socket;
72 int sock = 0;
74 unsigned xid;
75 unsigned int pos;
76 dhcp_request_t request;
78 if (!netif)
79 return DHCP_BAD_PARAMETERS;
81 request.buffer = kmalloc (DHCP_REQUEST_MAX_LEN);
83 if (!request.buffer)
84 return DHCP_OUT_OF_MEMORY;
86 char *buffer = kmalloc (1024);
88 if (!buffer) {
89 kfree (request.buffer);
90 return DHCP_OUT_OF_MEMORY;
93 net_ipv4 config_gw = 0x00000000;
94 net_ipv4 config_ip = 0x00000000;
95 net_ipv4 config_dns = 0x00000000;
96 net_ipv4 config_mask = 0x00000000;
97 net_ipv4 config_dhcp = 0x00000000;
98 unsigned int config_lease_time = 0;
100 int ret = 0;
102 /* Create a socket */
103 if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
104 DPRINT (DBG_DHCP, "DHCP client -> Can't create socket.");
105 ret = DHCP_SOCKET_FAILED;
106 goto clear;
109 server_socket.sin_family = AF_INET;
110 server_socket.sin_port = htons (67);
111 memcpy (&(server_socket.sin_addr), &server_addr, sizeof (net_ipv4));
113 /* Let's connect to server */
114 if (connect (sock, (sockaddr *) &server_socket,
115 sizeof (server_socket)) == -1) {
116 DPRINT (DBG_DHCP, "DHCP client -> Connection can't be estabilished");
117 ret = DHCP_CONNECT_FAILED;
118 goto clear;
121 net_proto_udp_anycast (sock);
122 net_proto_udp_port (sock, htons (68));
124 pos = dhcp_request_header_prepare (request, netif,
125 DHCP_MESSAGE_TYPE_BOOTREQUEST);
126 xid = request.bootp_header->xid;
128 buffer[0] = DHCP_OPTION_VALUE_DHCPDISCOVER;
129 pos = dhcp_request_option_add (request, DHCP_OPTION_MESSAGE_TYPE,
130 buffer, 1, pos);
132 buffer[0] = 0x01;
133 memcpy (buffer + 1, netif->dev->dev_addr, 6);
134 pos = dhcp_request_option_add (request, DHCP_OPTION_CLIENT_IDENTIFIER,
135 buffer, 7, pos);
137 char *hostname = hostname_get ();
139 if (strlen (hostname) <= 128) {
140 unsigned host_len = strlen (hostname);
141 pos = dhcp_request_option_add (request, DHCP_OPTION_HOSTNAME,
142 buffer, host_len, pos);
145 buffer[0] = DHCP_OPTION_VALUE_SUBNET_MASK;
146 buffer[1] = DHCP_OPTION_VALUE_ROUTER;
147 buffer[2] = DHCP_OPTION_VALUE_DNS_SERVER;
148 buffer[3] = DHCP_OPTION_VALUE_DOMAIN_NAME;
149 pos = dhcp_request_option_add (request, DHCP_OPTION_PARAMETER_REQUEST_LIST,
150 buffer, 0x04, pos);
152 int recv_ret;
154 DPRINT (DBG_DHCP, "DHCP client -> sending request");
155 if (send (sock, request.buffer, pos, 0) < 0) {
156 DPRINT (DBG_DHCP, "DHCP client -> Can't send request.");
157 ret = DHCP_CANT_SEND_REQUEST;
158 goto clear;
160 DPRINT (DBG_DHCP, "DHCP client -> reuquest sended");
162 recv_dhcpoffer:
163 config_ip = 0x00000000;
164 config_gw = 0x00000000;
165 config_dns = 0x00000000;
166 config_dhcp = 0x00000000;
168 DPRINT (DBG_DHCP, "DHCP client -> receiving response");
169 if ((recv_ret = recv (sock, request.buffer, DHCP_REQUEST_MAX_LEN, 0)) < sizeof (dhcp_bootp_header_t)) {
170 DPRINT (DBG_DHCP, "DHCP client -> Can't receive response.");
171 ret = DHCP_CANT_RECV_RESPONSE;
172 goto clear;
174 DPRINT (DBG_DHCP, "DHCP client -> response received");
176 if (request.bootp_header->mag_cookie != DHCP_MAGIC_COOKIE) {
177 DPRINT (DBG_DHCP, "DHCP client -> bad magic (finded 0x%X - needed 0x%X",
178 request.bootp_header->mag_cookie, DHCP_MAGIC_COOKIE);
179 ret = DHCP_BAD_PACKET;
180 goto clear;
183 pos = sizeof (dhcp_bootp_header_t);
185 config_ip = request.bootp_header->yiaddr;
187 if (request.bootp_header->xid != xid) {
188 DPRINT (DBG_DHCP, "DHCP client -> bad xid");
189 goto recv_dhcpoffer;
192 char dhcpack = 0;
193 while (pos < recv_ret) {
194 unsigned int option_len;
196 if (request.buffer[pos] == DHCP_OPTION_PAD) {
197 pos += 1;
198 continue;
201 if (request.buffer[pos] == DHCP_OPTION_END)
202 break;
204 if (pos >= recv_ret - 2) {
205 ret = DHCP_BAD_PACKET;
206 goto clear;
209 option_len = request.buffer[pos + 1];
210 pos += 2;
212 if (pos + option_len >= recv_ret) {
213 ret = DHCP_BAD_PACKET;
214 goto clear;
216 switch (request.buffer[pos - 2]) {
217 case DHCP_OPTION_MESSAGE_TYPE:
218 if (!option_len == 1) {
219 ret = DHCP_BAD_PACKET;
220 goto clear;
222 switch (request.buffer[pos]) {
223 case DHCP_OPTION_VALUE_DHCPOFFER:
224 break;
225 case DHCP_OPTION_VALUE_DHCPACK:
226 dhcpack = 1;
227 break;
228 case DHCP_OPTION_VALUE_DHCPNAK:
229 ret = DHCP_SRV_DIDNT_UNDERSTAND;
230 goto clear;
231 break;
232 default:
233 DPRINT (DBG_DHCP, "DHCP client -> bad DHCP message type");
234 goto recv_dhcpoffer;
236 break;
237 case DHCP_OPTION_SUBNET_MASK:
238 if (option_len >= 4)
239 memcpy (&config_mask, request.buffer + pos, sizeof (net_ipv4));
240 break;
241 case DHCP_OPTION_ROUTER:
242 if (option_len >= 4)
243 memcpy (&config_gw, request.buffer + pos, sizeof (net_ipv4));
244 break;
245 case DHCP_OPTION_DNS_SERVER:
246 if (option_len >= 4)
247 memcpy (&config_dns, request.buffer + pos, sizeof (net_ipv4));
248 break;
249 case DHCP_OPTION_DHCP_SERVER:
250 if (option_len >= 4)
251 memcpy (&config_dhcp, request.buffer + pos, sizeof (net_ipv4));
252 break;
253 case DHCP_OPTION_LEASE_TIME:
254 if (option_len >= 4)
255 memcpy (&config_lease_time, request.buffer + pos, sizeof (unsigned int));
256 break;
257 default:
258 break;
261 pos += option_len;
264 DPRINT (DBG_DHCP, "DHCP client -> response parsed");
266 if (dhcpack)
267 goto configure;
269 pos = dhcp_request_header_prepare (request, netif,
270 DHCP_MESSAGE_TYPE_BOOTREQUEST);
272 buffer[0] = DHCP_OPTION_VALUE_DHCPREQUEST;
273 pos = dhcp_request_option_add (request, DHCP_OPTION_MESSAGE_TYPE,
274 buffer, 1, pos);
276 memcpy (buffer, &config_ip, 4);
277 pos = dhcp_request_option_add (request, DHCP_OPTION_REQUESTED_IP,
278 buffer, 4, pos);
280 memcpy (buffer, &config_dhcp, 4);
281 pos = dhcp_request_option_add (request, DHCP_OPTION_DHCP_SERVER,
282 buffer, 4, pos);
284 DPRINT (DBG_DHCP, "DHCP client -> sending request");
285 if (send (sock, request.buffer, pos, 0) < 0) {
286 DPRINT (DBG_DHCP, "DHCP client -> Can't send request.");
287 ret = DHCP_CANT_SEND_REQUEST;
288 goto clear;
290 DPRINT (DBG_DHCP, "DHCP client -> request sended");
292 recv_dhcpack:
294 DPRINT (DBG_DHCP, "DHCP client -> receiving response");
295 if ((recv_ret = recv (sock, request.buffer, DHCP_REQUEST_MAX_LEN, 0)) < sizeof (dhcp_bootp_header_t)) {
296 DPRINT (DBG_DHCP, "DHCP client -> Can't receive response.");
297 ret = DHCP_CANT_RECV_RESPONSE;
298 goto clear;
300 DPRINT (DBG_DHCP, "DHCP client -> response received");
302 if (request.bootp_header->mag_cookie != DHCP_MAGIC_COOKIE) {
303 DPRINT (DBG_DHCP, "DHCP client -> bad magic (finded 0x%X - needed 0x%X",
304 request.bootp_header->mag_cookie, DHCP_MAGIC_COOKIE);
305 ret = DHCP_BAD_PACKET;
306 goto clear;
309 if (request.bootp_header->xid != xid) {
310 DPRINT (DBG_DHCP, "DHCP client -> bad xid");
311 goto recv_dhcpoffer;
314 pos = sizeof (dhcp_bootp_header_t);
316 while (pos < recv_ret) {
317 unsigned int option_len;
318 if (request.buffer[pos] == DHCP_OPTION_PAD) {
319 pos += 1;
320 continue;
323 if (request.buffer[pos] == DHCP_OPTION_END)
324 break;
326 if (pos >= recv_ret - 2) {
327 ret = DHCP_BAD_PACKET;
328 goto clear;
331 option_len = request.buffer[pos + 1];
332 pos += 2;
334 if (pos + option_len >= recv_ret) {
335 ret = DHCP_BAD_PACKET;
336 goto clear;
339 switch (request.buffer[pos - 2]) {
340 case DHCP_OPTION_MESSAGE_TYPE:
341 if (!option_len == 1) {
342 ret = DHCP_BAD_PACKET;
343 goto clear;
345 switch (request.buffer[pos]) {
346 case DHCP_OPTION_VALUE_DHCPOFFER:
347 ret = DHCP_BAD_PACKET;
348 goto clear;
349 break;
350 case DHCP_OPTION_VALUE_DHCPACK:
351 break;
352 case DHCP_OPTION_VALUE_DHCPNAK:
353 ret = DHCP_SRV_DIDNT_UNDERSTAND;
354 goto clear;
355 break;
356 default:
357 DPRINT (DBG_DHCP, "DHCP client -> bad DHCP message type");
358 goto recv_dhcpoffer;
360 break;
361 case DHCP_OPTION_SUBNET_MASK:
362 if (option_len >= 4)
363 memcpy (&config_mask, request.buffer + pos, sizeof (net_ipv4));
364 break;
365 case DHCP_OPTION_ROUTER:
366 if (option_len >= 4)
367 memcpy (&config_gw, request.buffer + pos, sizeof (net_ipv4));
368 break;
369 case DHCP_OPTION_DNS_SERVER:
370 if (option_len >= 4)
371 memcpy (&config_dns, request.buffer + pos, sizeof (net_ipv4));
372 break;
373 case DHCP_OPTION_LEASE_TIME:
374 if (option_len >= 4)
375 memcpy (&config_lease_time, request.buffer + pos, sizeof (unsigned int));
376 break;
377 default:
378 break;
381 pos += option_len;
384 configure:
385 DPRINT (DBG_DHCP, "DHCP client -> configuring interface");
387 char *ip_str = kmalloc (17); // 17 = IPv4 address in string maximum size + 1 zero
389 if (ip_str) {
390 memset (ip_str, 0, 17);
391 net_proto_ip_convert2 (config_ip, ip_str);
392 DPRINT (DBG_DHCP, "DHCP client -> my IP: %s", ip_str);
394 memset (ip_str, 0, 17);
395 net_proto_ip_convert2 (config_gw, ip_str);
396 DPRINT (DBG_DHCP, "DHCP client -> router IP: %s", ip_str);
398 memset (ip_str, 0, 17);
399 net_proto_ip_convert2 (config_dns, ip_str);
400 DPRINT (DBG_DHCP, "DHCP client -> DNS server IP: %s", ip_str);
402 DPRINT (DBG_DHCP, "DHCP client -> IP lease time: %ud seconds", config_lease_time);
403 kfree (ip_str);
405 dns_addr (config_dns);
406 netif_ip_addr (netif, config_ip, IF_CFG_TYPE_DHCP);
407 netif_gw_addr (netif, config_gw);
408 clear:
409 kfree (buffer);
410 kfree (request.buffer);
411 sclose (sock);
413 return ret;