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>
50 /**********************************************************************
51 *************************** LOCAL Structures *************************
52 **********************************************************************/
54 /**
55 * @brief
56 * The structure describes the BOOTP Master Control Block.
57 *
58 * @details
59 * The BOOTP Master control block stores information used by the
60 * BOOTP module.
61 */
62 typedef struct BOOTP_MCB
63 {
64 /**
65 * @brief This is the BOOTP header which is populated and sent across
66 * to the server.
67 */
68 BOOTPHDR boothdr;
70 /**
71 * @brief This is the BOOTP handle to the UDP socket.
72 */
73 Int32 sock;
75 /**
76 * @brief This is the number of BOOTP requests sent out.
77 */
78 Int32 num_request;
80 /**
81 * @brief This is the BOOTP timer handle.
82 */
83 Int32 bootp_timer;
85 /**
86 * @brief The optional application call back when the
87 * bootp file name and IP address has been received.
88 */
89 void (*asyncComplete)(void *);
91 }BOOTP_MCB;
93 /**********************************************************************
94 *************************** GLOBAL Variables *************************
95 **********************************************************************/
97 /**
98 * @brief This is the global master control block for the BOOTP module
99 * and stores all the necessary information.
100 */
101 BOOTP_MCB bootpmcb;
103 /**********************************************************************
104 **************************** BOOTP Functions *************************
105 **********************************************************************/
107 /**
108 * @b Description
109 * @n
110 * This is a call back function registered with the TIMER module
111 * to be called if there is a timeout and no BOOTP reply is
112 * received.
113 *
114 * @retval
115 * Not Applicable.
116 */
117 static void bootp_tmr_expiry (void)
118 {
119 BOOTPHDR* ptr_bootphdr;
121 /* Get the pointer to the BOOTP Header. */
122 ptr_bootphdr = &bootpmcb.boothdr;
124 /* Populate the BOOTP header with all the information we have. */
125 ptr_bootphdr->op = BOOTP_OP_REQUEST;
126 ptr_bootphdr->htype = BOOTP_HTYPE_ETHERNET;
127 ptr_bootphdr->hlen = 6;
128 ptr_bootphdr->xid = htonl(0x1);
129 memcpy ((void *)&ptr_bootphdr->chaddr, (void *)&netmcb.net_device.mac_address[0], 6);
131 /* The packet has been populated; send it to the server. */
132 udp_sock_send (bootpmcb.sock, (Uint8 *)ptr_bootphdr, sizeof(BOOTPHDR));
134 /* Increment the number of requests sent out. */
135 bootpmcb.num_request++;
137 /* We need to delete the current timer and create another with the backoff strategy. */
138 timer_delete (bootpmcb.bootp_timer);
140 /* Check if we have exceeded the max. permissible? */
141 if (bootpmcb.num_request > BOOTP_MAX_RETRIES)
142 {
143 /* Error: Maximum retransmissions have been exceeded; indicate error. */
144 mprintf ("BOOTP Failure: Max Retransmissions exceeded\n");
145 net_set_error ();
146 udp_sock_close(bootpmcb.sock);
147 return;
148 }
150 /* Create the new backoff timer. */
151 bootpmcb.bootp_timer = timer_add (BOOTP_SEED_TIMEOUT*bootpmcb.num_request, bootp_tmr_expiry);
152 if (bootpmcb.bootp_timer < 0)
153 {
154 /* Error: Unable to create the new backoff timer; indicate error. */
155 mprintf ("BOOTP Failure: Backoff timer failed\n");
156 net_set_error ();
157 stream_close();
158 udp_sock_close(bootpmcb.sock);
159 return;
160 }
161 return;
162 }
164 /**
165 * @b Description
166 * @n
167 * This is a call back function registered with the UDP module to
168 * be invoked when a BOOTP packet is received.
169 *
170 * @param[in] sock
171 * This is the socket handle on which packet was received.
172 * @param[in] ptr_data
173 * This is the pointer to the BOOTP data payload.
174 * @param[in] num_bytes
175 * This is the number of bytes of BOOTP data received.
176 *
177 * @retval
178 * Success - 0
179 * @retval
180 * Error - <0
181 */
182 static Int32 bootp_receive (Int32 sock, Uint8* ptr_data, Int32 num_bytes)
183 {
184 BOOTPHDR* ptr_bootphdr;
185 Int32 index = 0;
186 IPN subnetmask = BOOTP_DEFAULT_MASK;
187 IPN defaultRouter = 0;
188 IPN serverIP = 0;
190 /* Received a BOOTP packet from the UDP stack. */
191 ptr_bootphdr = (BOOTPHDR *)ptr_data;
193 /* Check if this is a BOOTP reply packet? */
194 if (ptr_bootphdr->op != BOOTP_OP_REPLY)
195 return -1;
197 /* Ensure the transaction id matches the one we sent out. */
198 if (ptr_bootphdr->xid != bootpmcb.boothdr.xid)
199 return -1;
201 /* Ensure the MAC Address matches our MAC Address */
202 if ((ptr_bootphdr->chaddr[0] != netmcb.net_device.mac_address[0]) ||
203 (ptr_bootphdr->chaddr[1] != netmcb.net_device.mac_address[1]) ||
204 (ptr_bootphdr->chaddr[2] != netmcb.net_device.mac_address[2]) ||
205 (ptr_bootphdr->chaddr[3] != netmcb.net_device.mac_address[3]) ||
206 (ptr_bootphdr->chaddr[4] != netmcb.net_device.mac_address[4]) ||
207 (ptr_bootphdr->chaddr[5] != netmcb.net_device.mac_address[5]))
208 {
209 /* The MAC Address do not match. Ignore the reply packet */
210 return -1;
211 }
213 /* Cycle through the options; we are only interested in retreiving the SUBNET Mask option
214 * Since we need to configure the routing table. */
215 while (index < 64)
216 {
217 /* Get the option tag and process it appropriately. */
218 switch (ptr_bootphdr->options[index])
219 {
220 case 0x0:
221 {
222 /* Padding option. Skip this. */
223 index++;
224 break;
225 }
226 case 0x1:
227 {
228 /* SUBNET option. Got it! We dont need to parse anymore. */
229 subnetmask = ((ptr_bootphdr->options[index+2] << 24) |
230 (ptr_bootphdr->options[index+3] << 16) |
231 (ptr_bootphdr->options[index+4] << 8) |
232 (ptr_bootphdr->options[index+5]));
234 /* Jump to the next option. */
235 index = index + ptr_bootphdr->options[index + 1] + 2;
236 break;
237 }
238 case 0x3:
239 {
240 /* ROUTER option. Got it! We dont need to parse anymore. */
241 defaultRouter = ((ptr_bootphdr->options[index+2] << 24) |
242 (ptr_bootphdr->options[index+3] << 16) |
243 (ptr_bootphdr->options[index+4] << 8) |
244 (ptr_bootphdr->options[index+5]));
246 /* Jump to the next option. */
247 index = index + ptr_bootphdr->options[index + 1] + 2;
248 break;
249 }
250 case 150:
251 {
252 /* TFTP Server IP Address: */
253 serverIP = ((ptr_bootphdr->options[index+2] << 24) |
254 (ptr_bootphdr->options[index+3] << 16) |
255 (ptr_bootphdr->options[index+4] << 8) |
256 (ptr_bootphdr->options[index+5]));
258 /* Convert to host order; so that it is in SYNC with "siaddr" field below. */
259 serverIP = ntohl(serverIP);
261 /* Jump to the next option. */
262 index = index + ptr_bootphdr->options[index + 1] + 2;
263 break;
264 }
265 case 0xFF:
266 {
267 /* End option. Terminate the loop. */
268 index = 64;
269 break;
270 }
271 default:
272 {
273 /* Any other option is not handled; but we need to skip it */
274 index = index + ptr_bootphdr->options[index + 1] + 2;
275 break;
276 }
277 }
278 }
280 /* The BOOTP Reply looks good. Kill the BOOTP timer. */
281 timer_delete (bootpmcb.bootp_timer);
283 /* Check if we have received the TFTP Server IP address or not? If not we assume
284 * that the TFTP Server and BOOTP Server address are one and the same. */
285 if (serverIP == 0x0)
286 serverIP = ptr_bootphdr->siaddr;
288 /* We have all the information with us from the BOOTP Reply Packet.
289 * a) IP Address
290 * b) Subnet Mask
291 * c) TFTP File Name.
292 * d) TFTP Server IP
293 * Lets configure the IPv4 Routing Table with the appropriate information and
294 * also configure the global netdevice structure. */
295 netmcb.net_device.ip_address = ptr_bootphdr->yiaddr;
297 if (netmcb.net_device.use_bootp_server_ip == TRUE)
298 netmcb.net_device.server_ip = serverIP;
300 netmcb.net_device.net_mask = subnetmask;
302 ip_add_route (FLG_RT_NETWORK, netmcb.net_device.ip_address, netmcb.net_device.net_mask, 0);
304 if (netmcb.net_device.use_bootp_file_name == TRUE)
305 memcpy (netmcb.net_device.file_name, ptr_bootphdr->file, sizeof(netmcb.net_device.file_name));
307 /* Check if we had received a default router? */
308 if (defaultRouter != 0)
309 ip_add_route (FLG_RT_DEFAULT, 0x0, 0x0, htonl(defaultRouter));
311 /* DEBUG Message: */
312 mprintf ("*****************************\n");
313 mprintf ("BOOTP Complete\n");
314 mprintf (" IP Address : 0x%x\n", ntohl(netmcb.net_device.ip_address));
315 mprintf (" Net Mask : 0x%x\n", subnetmask);
316 mprintf (" Default Router: 0x%x\n", defaultRouter);
317 mprintf (" Server IP : 0x%x\n", ntohl(serverIP));
318 mprintf (" File Name : %s\n", ptr_bootphdr->file);
319 mprintf ("*****************************\n");
321 /* Close the BOOTP sockets. */
322 stream_close();
323 udp_sock_close (sock);
325 /* Optional call back with bootp params */
326 if (bootpmcb.asyncComplete != NULL)
327 (*bootpmcb.asyncComplete)((void *)&netmcb.net_device);
329 /* Initiate the TFTP Transfer. */
330 tftp_get_file (netmcb.net_device.server_ip, (char *)netmcb.net_device.file_name);
332 /* BOOTP Reply has been processed. */
333 return 0;
334 }
336 /**
337 * @b Description
338 * @n
339 * The function is used to initialize the BOOTP client.
340 *
341 * @retval
342 * Not Applicable.
343 */
344 void bootp_init (void (*asyncComplete)(void *))
345 {
346 BOOTPHDR* ptr_bootphdr;
347 SOCKET socket;
349 /* Initialize the BOOT MCB */
350 memset ((void *)&bootpmcb, 0, sizeof(BOOTP_MCB));
352 bootpmcb.asyncComplete = asyncComplete;
355 /* Populate the socket structure and register this with the UDP module. */
356 socket.local_port = BOOTP_CLIENT_PORT;
357 socket.remote_port = BOOTP_SERVER_PORT;
358 socket.remote_address = 0xFFFFFFFF;
359 socket.app_fn = bootp_receive;
361 /* Open the BOOTP socket. */
362 bootpmcb.sock = udp_sock_open (&socket);
363 if (bootpmcb.sock < 0)
364 {
365 /* Error: Socket could not be opened. */
366 mprintf ("ERROR: BOOTP SOCK Open Failed\n");
367 net_set_error ();
368 return;
369 }
371 /* Open the stream to receive packets */
372 stream_open(TFTP_DATA_SIZE);
374 /* Get the pointer to the BOOTP Header. */
375 ptr_bootphdr = &bootpmcb.boothdr;
377 /* Populate the BOOTP header with all the information we have. */
378 ptr_bootphdr->op = BOOTP_OP_REQUEST;
379 ptr_bootphdr->htype = BOOTP_HTYPE_ETHERNET;
380 ptr_bootphdr->hlen = 6;
381 ptr_bootphdr->xid = htonl(0x1);
382 memcpy ((void *)&ptr_bootphdr->chaddr, (void *)&netmcb.net_device.mac_address[0], 6);
384 /* The packet has been populated; send it to the server. */
385 udp_sock_send (bootpmcb.sock, (Uint8 *)ptr_bootphdr, sizeof(BOOTPHDR));
387 /* Increment the number of requests sent out. */
388 bootpmcb.num_request++;
390 /* Create the BOOTP Timer; if timer creation fails then BOOTP Retransmissions
391 * will not work and so we treat this as a fatal error. */
392 bootpmcb.bootp_timer = timer_add (BOOTP_SEED_TIMEOUT, bootp_tmr_expiry);
393 if (bootpmcb.bootp_timer < 0)
394 {
395 /* Error: Timer could not be created; we need to close the socket and signal error. */
396 mprintf ("ERROR: BOOTP ADD Timer Failed\n");
397 stream_close();
398 udp_sock_close (bootpmcb.sock);
399 net_set_error ();
400 return;
401 }
402 return;
403 }