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