1 /**
2 * @file bootp.c
3 *
4 * @brief
5 * The file implements the NET Module BOOTP functionality.
6 *
7 * \par
8 * NOTE:
9 * (C) Copyright 2008, Texas Instruments, Inc.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 *
18 * Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the
21 * distribution.
22 *
23 * Neither the name of Texas Instruments Incorporated nor the names of
24 * its contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 *
39 * \par
40 */
41 #include "types.h"
42 #include "iblloc.h"
43 #include "net.h"
44 #include "netif.h"
45 #include "timer.h"
46 #include "stream.h"
47 #include <string.h>
48 #include "net_osal.h"
51 /**********************************************************************
52 *************************** LOCAL Structures *************************
53 **********************************************************************/
55 /**
56 * @brief
57 * The structure describes the BOOTP Master Control Block.
58 *
59 * @details
60 * The BOOTP Master control block stores information used by the
61 * BOOTP module.
62 */
63 typedef struct BOOTP_MCB
64 {
65 /**
66 * @brief This is the BOOTP header which is populated and sent across
67 * to the server.
68 */
69 BOOTPHDR boothdr;
71 /**
72 * @brief This is the BOOTP handle to the UDP socket.
73 */
74 Int32 sock;
76 /**
77 * @brief This is the number of BOOTP requests sent out.
78 */
79 Int32 num_request;
81 /**
82 * @brief This is the BOOTP timer handle.
83 */
84 Int32 bootp_timer;
86 /**
87 * @brief The optional application call back when the
88 * bootp file name and IP address has been received.
89 */
90 void (*asyncComplete)(void *);
92 }BOOTP_MCB;
94 /**********************************************************************
95 *************************** GLOBAL Variables *************************
96 **********************************************************************/
98 /**
99 * @brief This is the global master control block for the BOOTP module
100 * and stores all the necessary information.
101 */
102 BOOTP_MCB bootpmcb;
104 /**********************************************************************
105 **************************** BOOTP Functions *************************
106 **********************************************************************/
108 /**
109 * @b Description
110 * @n
111 * This is a call back function registered with the TIMER module
112 * to be called if there is a timeout and no BOOTP reply is
113 * received.
114 *
115 * @retval
116 * Not Applicable.
117 */
118 static void bootp_tmr_expiry (void)
119 {
120 BOOTPHDR* ptr_bootphdr;
122 /* Get the pointer to the BOOTP Header. */
123 ptr_bootphdr = &bootpmcb.boothdr;
125 /* Populate the BOOTP header with all the information we have. */
126 ptr_bootphdr->op = BOOTP_OP_REQUEST;
127 ptr_bootphdr->htype = BOOTP_HTYPE_ETHERNET;
128 ptr_bootphdr->hlen = 6;
129 ptr_bootphdr->xid = htonl(0x1);
130 netMemcpy ((void *)&ptr_bootphdr->chaddr, (void *)&netmcb.net_device.mac_address[0], 6);
132 /* The packet has been populated; send it to the server. */
133 udp_sock_send (bootpmcb.sock, (Uint8 *)ptr_bootphdr, sizeof(BOOTPHDR));
135 /* Increment the number of requests sent out. */
136 bootpmcb.num_request++;
138 /* We need to delete the current timer and create another with the backoff strategy. */
139 timer_delete (bootpmcb.bootp_timer);
141 /* Check if we have exceeded the max. permissible? */
142 if (bootpmcb.num_request > BOOTP_MAX_RETRIES)
143 {
144 /* Error: Maximum retransmissions have been exceeded; indicate error. */
145 mprintf ("BOOTP Failure: Max Retransmissions exceeded\n");
146 net_set_error ();
147 udp_sock_close(bootpmcb.sock);
148 return;
149 }
151 /* Create the new backoff timer. */
152 bootpmcb.bootp_timer = timer_add (BOOTP_SEED_TIMEOUT*bootpmcb.num_request, bootp_tmr_expiry);
153 if (bootpmcb.bootp_timer < 0)
154 {
155 /* Error: Unable to create the new backoff timer; indicate error. */
156 mprintf ("BOOTP Failure: Backoff timer failed\n");
157 net_set_error ();
158 stream_close();
159 udp_sock_close(bootpmcb.sock);
160 return;
161 }
162 return;
163 }
165 /**
166 * @b Description
167 * @n
168 * This is a call back function registered with the UDP module to
169 * be invoked when a BOOTP packet is received.
170 *
171 * @param[in] sock
172 * This is the socket handle on which packet was received.
173 * @param[in] ptr_data
174 * This is the pointer to the BOOTP data payload.
175 * @param[in] num_bytes
176 * This is the number of bytes of BOOTP data received.
177 *
178 * @retval
179 * Success - 0
180 * @retval
181 * Error - <0
182 */
183 static Int32 bootp_receive (Int32 sock, Uint8* ptr_data, Int32 num_bytes)
184 {
185 BOOTPHDR* ptr_bootphdr;
186 Int32 index = 0;
187 IPN subnetmask = BOOTP_DEFAULT_MASK;
188 IPN defaultRouter = 0;
189 IPN serverIP = 0;
191 /* Received a BOOTP packet from the UDP stack. */
192 ptr_bootphdr = (BOOTPHDR *)ptr_data;
194 /* Check if this is a BOOTP reply packet? */
195 if (ptr_bootphdr->op != BOOTP_OP_REPLY)
196 return -1;
198 /* Ensure the transaction id matches the one we sent out. */
199 if (ptr_bootphdr->xid != bootpmcb.boothdr.xid)
200 return -1;
202 /* Ensure the MAC Address matches our MAC Address */
203 if ((ptr_bootphdr->chaddr[0] != netmcb.net_device.mac_address[0]) ||
204 (ptr_bootphdr->chaddr[1] != netmcb.net_device.mac_address[1]) ||
205 (ptr_bootphdr->chaddr[2] != netmcb.net_device.mac_address[2]) ||
206 (ptr_bootphdr->chaddr[3] != netmcb.net_device.mac_address[3]) ||
207 (ptr_bootphdr->chaddr[4] != netmcb.net_device.mac_address[4]) ||
208 (ptr_bootphdr->chaddr[5] != netmcb.net_device.mac_address[5]))
209 {
210 /* The MAC Address do not match. Ignore the reply packet */
211 return -1;
212 }
214 /* Cycle through the options; we are only interested in retreiving the SUBNET Mask option
215 * Since we need to configure the routing table. */
216 while (index < 64)
217 {
218 /* Get the option tag and process it appropriately. */
219 switch (ptr_bootphdr->options[index])
220 {
221 case 0x0:
222 {
223 /* Padding option. Skip this. */
224 index++;
225 break;
226 }
227 case 0x1:
228 {
229 /* SUBNET option. Got it! We dont need to parse anymore. */
230 subnetmask = ((ptr_bootphdr->options[index+2] << 24) |
231 (ptr_bootphdr->options[index+3] << 16) |
232 (ptr_bootphdr->options[index+4] << 8) |
233 (ptr_bootphdr->options[index+5]));
235 /* Jump to the next option. */
236 index = index + ptr_bootphdr->options[index + 1] + 2;
237 break;
238 }
239 case 0x3:
240 {
241 /* ROUTER option. Got it! We dont need to parse anymore. */
242 defaultRouter = ((ptr_bootphdr->options[index+2] << 24) |
243 (ptr_bootphdr->options[index+3] << 16) |
244 (ptr_bootphdr->options[index+4] << 8) |
245 (ptr_bootphdr->options[index+5]));
247 /* Jump to the next option. */
248 index = index + ptr_bootphdr->options[index + 1] + 2;
249 break;
250 }
251 case 150:
252 {
253 /* TFTP Server IP Address: */
254 serverIP = ((ptr_bootphdr->options[index+2] << 24) |
255 (ptr_bootphdr->options[index+3] << 16) |
256 (ptr_bootphdr->options[index+4] << 8) |
257 (ptr_bootphdr->options[index+5]));
259 /* Convert to host order; so that it is in SYNC with "siaddr" field below. */
260 serverIP = ntohl(serverIP);
262 /* Jump to the next option. */
263 index = index + ptr_bootphdr->options[index + 1] + 2;
264 break;
265 }
266 case 0xFF:
267 {
268 /* End option. Terminate the loop. */
269 index = 64;
270 break;
271 }
272 default:
273 {
274 /* Any other option is not handled; but we need to skip it */
275 index = index + ptr_bootphdr->options[index + 1] + 2;
276 break;
277 }
278 }
279 }
281 /* The BOOTP Reply looks good. Kill the BOOTP timer. */
282 timer_delete (bootpmcb.bootp_timer);
284 /* Check if we have received the TFTP Server IP address or not? If not we assume
285 * that the TFTP Server and BOOTP Server address are one and the same. */
286 if (serverIP == 0x0)
287 serverIP = ptr_bootphdr->siaddr;
289 /* We have all the information with us from the BOOTP Reply Packet.
290 * a) IP Address
291 * b) Subnet Mask
292 * c) TFTP File Name.
293 * d) TFTP Server IP
294 * Lets configure the IPv4 Routing Table with the appropriate information and
295 * also configure the global netdevice structure. */
296 netmcb.net_device.ip_address = ptr_bootphdr->yiaddr;
298 if (netmcb.net_device.use_bootp_server_ip == TRUE)
299 netmcb.net_device.server_ip = serverIP;
301 netmcb.net_device.net_mask = subnetmask;
303 ip_add_route (FLG_RT_NETWORK, netmcb.net_device.ip_address, netmcb.net_device.net_mask, 0);
305 if (netmcb.net_device.use_bootp_file_name == TRUE)
306 netMemcpy (netmcb.net_device.file_name, ptr_bootphdr->file, sizeof(netmcb.net_device.file_name));
308 /* Check if we had received a default router? */
309 if (defaultRouter != 0)
310 ip_add_route (FLG_RT_DEFAULT, 0x0, 0x0, htonl(defaultRouter));
312 /* DEBUG Message: */
313 mprintf ("*****************************\n");
314 mprintf ("BOOTP Complete\n");
315 mprintf (" IP Address : 0x%x\n", ntohl(netmcb.net_device.ip_address));
316 mprintf (" Net Mask : 0x%x\n", subnetmask);
317 mprintf (" Default Router: 0x%x\n", defaultRouter);
318 mprintf (" Server IP : 0x%x\n", ntohl(serverIP));
319 mprintf (" File Name : %s\n", ptr_bootphdr->file);
320 mprintf ("*****************************\n");
322 /* Close the BOOTP sockets. */
323 stream_close();
324 udp_sock_close (sock);
326 /* Optional call back with bootp params */
327 if (bootpmcb.asyncComplete != NULL)
328 (*bootpmcb.asyncComplete)((void *)&netmcb.net_device);
330 /* Initiate the TFTP Transfer. */
331 tftp_get_file (netmcb.net_device.server_ip, (char *)netmcb.net_device.file_name);
333 /* BOOTP Reply has been processed. */
334 return 0;
335 }
337 /**
338 * @b Description
339 * @n
340 * The function is used to initialize the BOOTP client.
341 *
342 * @retval
343 * Not Applicable.
344 */
345 void bootp_init (void (*asyncComplete)(void *))
346 {
347 BOOTPHDR* ptr_bootphdr;
348 SOCKET socket;
350 /* Initialize the BOOT MCB */
351 netMemset ((void *)&bootpmcb, 0, sizeof(BOOTP_MCB));
353 bootpmcb.asyncComplete = asyncComplete;
356 /* Populate the socket structure and register this with the UDP module. */
357 socket.local_port = BOOTP_CLIENT_PORT;
358 socket.remote_port = BOOTP_SERVER_PORT;
359 socket.remote_address = 0xFFFFFFFF;
360 socket.app_fn = bootp_receive;
362 /* Open the BOOTP socket. */
363 bootpmcb.sock = udp_sock_open (&socket);
364 if (bootpmcb.sock < 0)
365 {
366 /* Error: Socket could not be opened. */
367 mprintf ("ERROR: BOOTP SOCK Open Failed\n");
368 net_set_error ();
369 return;
370 }
372 /* Open the stream to receive packets */
373 stream_open(TFTP_DATA_SIZE);
375 /* Get the pointer to the BOOTP Header. */
376 ptr_bootphdr = &bootpmcb.boothdr;
378 /* Populate the BOOTP header with all the information we have. */
379 ptr_bootphdr->op = BOOTP_OP_REQUEST;
380 ptr_bootphdr->htype = BOOTP_HTYPE_ETHERNET;
381 ptr_bootphdr->hlen = 6;
382 ptr_bootphdr->xid = htonl(0x1);
383 netMemcpy ((void *)&ptr_bootphdr->chaddr, (void *)&netmcb.net_device.mac_address[0], 6);
385 /* The packet has been populated; send it to the server. */
386 udp_sock_send (bootpmcb.sock, (Uint8 *)ptr_bootphdr, sizeof(BOOTPHDR));
388 /* Increment the number of requests sent out. */
389 bootpmcb.num_request++;
391 /* Create the BOOTP Timer; if timer creation fails then BOOTP Retransmissions
392 * will not work and so we treat this as a fatal error. */
393 bootpmcb.bootp_timer = timer_add (BOOTP_SEED_TIMEOUT, bootp_tmr_expiry);
394 if (bootpmcb.bootp_timer < 0)
395 {
396 /* Error: Timer could not be created; we need to close the socket and signal error. */
397 mprintf ("ERROR: BOOTP ADD Timer Failed\n");
398 stream_close();
399 udp_sock_close (bootpmcb.sock);
400 net_set_error ();
401 return;
402 }
403 return;
404 }