Initial c661x version
[keystone-rtos/ibl.git] / src / driver / eth / bootp.c
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)
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;
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)
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;
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 *))
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;