source: edk_user_repository/WARP/sw_services/WARP_ip_udp_v1_00_a/src/WARP_ip_udp_ip_udp.c

Last change on this file was 5969, checked in by murphpo, 7 years ago

Fixed MAC addr length macro name

File size: 36.6 KB
Line 
1/** @file  WARP_ip_udp_ip_udp.c
2 *  @brief WARP IP/UDP Library (IP/UDP/ARP/IMCP Processing)
3 *
4 *  @copyright Copyright 2015, Mango Communications. All rights reserved.
5 *          Distributed under the WARP license  (http://warpproject.org/license)
6 *
7 *  @author Chris Hunter (chunter [at] mangocomm.com)
8 *  @author Patrick Murphy (murphpo [at] mangocomm.com)
9 *  @author Erik Welsh (welsh [at] mangocomm.com)
10 */
11
12/***************************** Include Files *********************************/
13
14// Xilinx / Standard library includes
15#include <xparameters.h>
16#include <xstatus.h>
17#include <xil_io.h>
18#include <stdlib.h>
19#include <stdio.h>
20
21// WARP IP/UDP Library includes
22#include "WARP_ip_udp.h"
23#include "WARP_ip_udp_internal.h"
24
25
26/*************************** Constant Definitions ****************************/
27
28
29
30/*********************** Global Variable Definitions *************************/
31
32
33
34/*************************** Variable Definitions ****************************/
35
36u16           ipv4_id_counter = 0;
37
38
39
40/*************************** Function Prototypes *****************************/
41
42void arp_reply(u32 eth_dev_num, warp_ip_udp_buffer * arp_request);
43
44void imcp_echo_reply(u32 eth_dev_num, warp_ip_udp_buffer * echo_request);
45
46
47/******************************** Functions **********************************/
48
49
50/**********************************************************************************************************************/
51/**
52 * @brief IP Functions
53 *
54 **********************************************************************************************************************/
55
56
57/*****************************************************************************/
58/**
59 * Initialize the IPv4 global variables
60 *
61 * @param   None
62 *
63 * @return  None
64 *
65 *****************************************************************************/
66void ipv4_init() {
67
68    // Initialize the ID counter
69    ipv4_id_counter = 0;
70
71}
72
73
74
75/*****************************************************************************/
76/**
77 * Process the IP packet
78 *
79 * @param   eth_dev_num - Ethernet device the message came in on
80 * @param   packet      - WARP UDP IP/UDP Buffer containing the IP packet
81 *
82 * @return  int         - Number of bytes of data in the processed packet; 0 if the packet could not be processed
83 *
84 * @note    This function assumes that both Ethernet device and buffer are valid.
85 *
86 *****************************************************************************/
87int ipv4_process_packet(u32 eth_dev_num, warp_ip_udp_buffer * packet) {
88
89    ipv4_header       * header         = (ipv4_header *) packet->offset;
90
91    u32                 addr_check     = 0;
92    u8                * src_ip_addr    = (u8 *)&(header->src_ip_addr);
93    u8                * dest_ip_addr   = (u8 *)&(header->dest_ip_addr);
94
95    u8                * my_ip_addr     = eth_device[eth_dev_num].ip_addr;
96
97    // Get the Ethernet header so we have the source MAC address for the ARP cache
98    ethernet_header   * eth_header     = (ethernet_header *) packet->data;
99
100    // Check the address of the IP packet
101    //     - If the node has not been initialized (eg the node address is 10.0.0.0), then accept broadcast packets from 10.0.X.255
102    //     - If the node has been initialized, then accept unicast packets and broadcast packets on the given subnet
103    //
104    //
105    // !!! TBD !!! - Future addition:  The address check should really be more configurable (ie it should be a callback
106    //                                 that can be set by the application that uses the library).
107    //
108    //
109    if (my_ip_addr[3] == 0) {
110
111        // Accept broadcast packets from 10.0.X.255
112        if ((my_ip_addr[0] == dest_ip_addr[0]) && (my_ip_addr[1] == dest_ip_addr[1]) && (dest_ip_addr[3] == 255)) {
113             addr_check = 1;
114        }
115    } else {
116        // Accept unicast packets and broadcast packets on the given subnet
117        if ((my_ip_addr[0] == dest_ip_addr[0]) &&
118            (my_ip_addr[1] == dest_ip_addr[1]) &&
119            (my_ip_addr[2] == dest_ip_addr[2]) &&
120            ((my_ip_addr[3] == dest_ip_addr[3]) || (dest_ip_addr[3] == 255))) {
121            addr_check = 1;
122        }
123    }
124
125    //
126    //
127    // !!! TBD !!! - Future consideration:  Additional packet checks - Length & Checksum
128    //     u16            packet_length  = packet->length;
129    //     u16            ip_length      = Xil_Ntohs(header->total_length);
130    //
131    //
132   
133    if (addr_check == 1) {
134   
135        // The Xilinx Ethernet / DMA hardware does not support fragmented Ethernet packets.  However, the
136        // library will still pass the first fragment of a packet up to the higher level transport for
137        // processing so that the host that sent the fragmented packet does not have a transport timeout
138        // (This is important when trying to determine the maximum packet size supported by the transport). 
139        // If this behavior needs to change the below code will cause the WARP IP/UDP Library to discard
140        // packet fragments.
141        //
142        // The 'fragment offset field' is 16 bits (see http://en.wikipedia.org/wiki/IPv4#Packet_structure
143        // for more information).  The 'DF' flag can be legitimately set to '1' so we need to mask that
144        // bit before we decide if we can discard the packet.  This means that the frag_off field can
145        // have valid values of either 0x0000 or 0x4000 (big endian).  However, we have to convert to
146        // little endian so the valid values are 0x0000 and 0x0040 (ie byte swapped).
147        //
148        // if ((header->fragment_offset & 0xFFBF) != 0x0000) {
149        //     xil_printf("ERROR:  Library does not support fragmented packets.\n");
150        //     return 0;
151        // }
152
153        // Update ARP table (Maps IP address to MAC address)
154        arp_update_cache(eth_dev_num, eth_header->src_mac_addr, src_ip_addr);
155       
156        // Update the offset within the buffer to after the IP header
157        packet->offset += IP_HEADER_LEN_BYTES;
158        packet->length -= IP_HEADER_LEN_BYTES;
159
160        // Process the IP packet
161        switch (header->protocol) {
162            // UDP packets
163            case IP_PROTOCOL_UDP:
164                return udp_process_packet(eth_dev_num, packet);
165            break;
166           
167            // IMCP packets
168            case IP_PROTOCOL_IMCP:
169                return imcp_process_packet(eth_dev_num, packet);
170            break;
171           
172            // If a packet has made it here, the it is destined for the node but cannot be processed
173            // by the library.  Therefore, we need to print an error message.
174            default:
175                xil_printf("ERROR:  Unknown IP protocol:  %d\n", header->protocol);
176            break;
177        }
178    }
179
180    return 0;
181}
182
183
184
185/*****************************************************************************/
186/**
187 * Initialize the IP Header
188 *
189 * @param   header           - Pointer to the IP header
190 * @param   src_ip_addr      - Source IP address for IP packet (
191 *
192 * @return  None
193 *
194 *****************************************************************************/
195void ipv4_init_header(ipv4_header * header, u8 * src_ip_addr) {
196
197    u32 ip_addr;
198
199    // Update the following fields because they are static for the header:
200    //   - Version / Internet Header Length
201    //   - DSCP / ECN
202    //   - TTL
203    //   - Fragmentation offset
204    //   - Source IP address
205    //
206    header->version_ihl      = (IP_VERSION_4 << 4) +  IP_HEADER_LEN;
207    header->dscp_ecn         = (IP_DSCP_CS0 << 2) + IP_ECN_NON_ECT;
208    header->fragment_offset  = IP_NO_FRAGMENTATION;
209    header->ttl              = IP_DEFAULT_TTL;
210   
211    // Convert IP address to u32 (big endian)
212    ip_addr = (src_ip_addr[3] << 24) + (src_ip_addr[2] << 16) + (src_ip_addr[1] << 8) + src_ip_addr[0];
213
214    header->src_ip_addr      = ip_addr;
215}
216
217
218
219/*****************************************************************************/
220/**
221 * Update the IP Header
222 *
223 * @param   header           - Pointer to the IP header
224 * @param   dest_ip_addr     - Destination IP address for IP packet (big-endian)
225 * @param   ip_length        - Length of the IP packet (includes IP header) (little-endian)
226 * @param   protocol         - Protocol of the IP packet
227 *
228 * @return  None
229 *
230 *****************************************************************************/
231void ipv4_update_header(ipv4_header * header, u32 dest_ip_addr, u16 ip_length, u8 protocol) {
232
233    // Update the following fields:
234    //   - Length
235    //   - Identification
236    //   - Protocol
237    //   - Checksum
238    //   - Destination IP address
239    //
240    // NOTE:  We do not need to update the following fields because they are static for the socket:
241    //   - Version / Internet Header Length
242    //   - DSCP / ECN
243    //   - TTL
244    //   - Source IP address
245    //
246    header->total_length     = Xil_Htons(ip_length);
247    header->identification   = Xil_Htons(ipv4_id_counter++);
248    header->protocol         = protocol;
249    header->header_checksum  = 0;                                    // Set to zero for checksum calculation   
250    header->dest_ip_addr     = dest_ip_addr;
251   
252    // Update the checksum with 1's complement of 1's complement 16-bit sum
253    header->header_checksum  = Xil_Htons(ipv4_compute_checksum((u8 *)header, sizeof(ipv4_header)));
254}
255
256
257
258/*****************************************************************************/
259/**
260 * Compute IP Checksum
261 *
262 * The ones' complement of the ones' complement sum of the data's 16-bit words
263 *
264 * @param   data            - Pointer to the data words
265 * @param   size            - Size of the data to use
266 *
267 * @return  u16             - Checksum value
268 *
269 * @note    IP Checksum Algorithm:  http://en.wikipedia.org/wiki/IPv4_header_checksum
270 *
271 *****************************************************************************/
272u16 ipv4_compute_checksum(u8 * data, u32 size) {
273
274    u32   i;
275    u32   sum  = 0;
276    u16   word = 0;
277   
278    // Sum all 16-bit words in the header (big-endian)
279    for (i = 0; i < size; i = i + 2) {
280        word = ((data[i] << 8) & 0xFF00) + (data[i+1] & 0x00FF);
281        sum  = sum + ((u32) word);
282    }
283
284    // 1's complement 16-bit sum, formed by "end around carry" of 32-bit 2's complement sum
285    sum = ((sum & 0xFFFF0000) >> 16) + (sum & 0x0000FFFF);
286
287    // Return the 1's complement of 1's complement 16-bit sum
288    return (~sum);
289}
290
291 
292
293
294/**********************************************************************************************************************/
295/**
296 * @brief UDP Functions
297 *
298 **********************************************************************************************************************/
299
300
301/*****************************************************************************/
302/**
303 * Process the UDP packet
304 *
305 * @param   eth_dev_num - Ethernet device the message came in on
306 * @param   packet      - WARP IP/UDP Buffer containing the UDP packet
307 *
308 * @return  int         - Number of bytes of data in the UDP packet; 0 if the packet could not be processed
309 *
310 * @note    This function assumes that both Ethernet device and buffer are valid.
311 *
312 *****************************************************************************/
313int udp_process_packet(u32 eth_dev_num, warp_ip_udp_buffer * packet) {
314
315    udp_header   * header         = (udp_header *) packet->offset;   
316   
317    u32            port_check     = 0;
318    u16            dest_port      = Xil_Ntohs(header->dest_port);
319
320    // See if there is a socket that corresponds to the UDP packet
321    //     - Check all open sockets to see if one matches the port / eth_dev_num
322    //
323    if (socket_find_index_by_eth(eth_dev_num, dest_port) != SOCKET_INVALID_SOCKET) {
324   
325        port_check = 1;
326    }
327   
328    //
329    //
330    // !!! TBD !!! - Future consideration:  Additional packet checks - Length & Checksum
331    //     u16            packet_length  = packet->length;
332    //     u16            udp_length     = Xil_Ntohs(header->length);
333    //
334    //
335
336    if (port_check == 1) {
337
338        // Update the offset within the buffer to after the UDP header
339        packet->offset += UDP_HEADER_LEN;
340        packet->length -= UDP_HEADER_LEN;
341
342        // Return the length of the remaining data bytes
343        return packet->length;
344    }
345
346    return 0;
347}
348
349
350
351/*****************************************************************************/
352/**
353 * Initialize the UDP Header
354 *
355 * @param   header           - Pointer to the UDP header
356 * @param   src_port         - Source port for UDP packet
357 *
358 * @return  None
359 *
360 *****************************************************************************/
361void udp_init_header(udp_header * header, u16 src_port) {
362
363    // Update the following fields that are static for the socket:
364    //   - Source port
365    //
366    header->src_port  = Xil_Htons(src_port);
367}
368
369
370
371/*****************************************************************************/
372/**
373 * Update the UDP Header
374 *
375 * @param   header           - Pointer to the UDP header
376 * @param   dest_port        - Destination port for UDP packet (big endian)
377 * @param   udp_length       - Length of the UDP packet (includes UDP header)
378 *
379 * @return  None
380 *
381 *****************************************************************************/
382void udp_update_header(udp_header * header, u16 dest_port, u16 udp_length) {
383
384    // Update the following fields:
385    //   - Destination port
386    //   - Length
387    //
388    // NOTE:  We do not need to update the following fields because they are static for the socket:
389    //   - Source port
390    //
391    header->dest_port = dest_port;
392    header->length    = Xil_Htons(udp_length);
393   
394    // Currently, the WARP IP/UDP Library does not use the UDP checksum capabilities.  This is primarily
395    // due to the amount of time required to compute the checksum.  Also, given that communication
396    // between hosts and WARP nodes is, in general, fairly localized, there is not as much of a need for
397    // the data integrity check that the UDP checksum provides.
398    //
399    header->checksum  = UDP_NO_CHECKSUM;
400}
401
402
403
404/**********************************************************************************************************************/
405/**
406 * @brief ARP Functions
407 *
408 **********************************************************************************************************************/
409
410 
411/*****************************************************************************/
412/**
413 * Process the ARP packet
414 *
415 * @param   eth_dev_num - Ethernet device the message came in on
416 * @param   packet      - WARP IP/UDP Buffer containing the ARP packet
417 *
418 * @return  int         - Always returns 0 since we don't want higher level transports
419 *                        to process this packet
420 *
421 * @note    This function assumes that both Ethernet device and buffer are valid.
422 *
423 *****************************************************************************/
424int arp_process_packet(u32 eth_dev_num, warp_ip_udp_buffer * packet) {
425
426    arp_ipv4_packet   * arp            = (arp_ipv4_packet *) packet->offset;
427    u8                * my_ip_addr     = eth_device[eth_dev_num].ip_addr;
428   
429    // Process the ARP packet
430    //     - If the ARP is a request to the node, then update the ARP table and send a reply
431    //     - If the ARP is a reply, then update the ARP table
432    //
433    // NOTE:  The library does not currently process gratuitous ARPs since there are a limited number
434    //        of ARP table entries.  However, this functionality would be easy to add in.
435    //
436    if ((Xil_Ntohs(arp->htype) == ARP_HTYPE_ETH)    &&                         // Hardware type is "Ethernet"
437        (Xil_Ntohs(arp->ptype) == ETHERTYPE_IP_V4)  &&                         // Protocol type is "IP v4"
438        (arp->hlen             == ETH_MAC_ADDR_LEN) &&                         // Hardware length is "Ethernet"
439        (arp->plen             == IP_ADDR_LEN)      ) {                        // Protocol length is "IP v4"
440
441        //
442        //
443        // !!! TBD !!! - Future addition:  To process gratuitous ARPs, check:
444        //     - ARP Request and target_paddr == sender_paddr and target_haddr == {0, 0, 0, 0, 0, 0}
445        //     - ARP Reply   and target_paddr == sender_paddr and target_haddr == sender_haddr
446        //
447        //
448
449        // Check the ARP is for the node
450        //     NOTE:  For ARP requests, the target haddr is ignored. 
451        //
452        if ((arp->target_paddr[0]  == my_ip_addr[0])    &&                         // IP address matches node IP address
453            (arp->target_paddr[1]  == my_ip_addr[1])    && 
454            (arp->target_paddr[2]  == my_ip_addr[2])    && 
455            (arp->target_paddr[3]  == my_ip_addr[3])    ) {
456
457            // Update the ARP table regardless of whether this is a request or a reply
458            arp_update_cache(eth_dev_num, arp->sender_haddr, arp->sender_paddr);
459
460            // Process the ARP operation
461            //     NOTE:  In the case of an ARP reply, we have already updated the ARP cache, so there is nothing
462            //            further to be done.  Therefore, we are just using an if statement to check if this is
463            //            an ARP request.  If needed, this can be changed to a case statement to process other ARP
464            //            operations. 
465            //
466            if (Xil_Ntohs(arp->oper) == ARP_REQUEST) {
467                // Send an ARP reply
468                arp_reply(eth_dev_num, packet);
469            }
470        }
471    }
472
473    return 0;    // Upper layer stacks should not process this packet so return zero bytes
474}
475
476
477
478/*****************************************************************************/
479/**
480 * Send an ARP Reply
481 *
482 * @param   eth_dev_num - Ethernet device the message came in on
483 * @param   arp_request - WARP IP/UDP Buffer containing the ARP packet
484 *
485 * @return  None
486 *
487 * @note    This function assumes that both Ethernet device and buffer are valid.
488 *
489 *****************************************************************************/
490void arp_reply(u32 eth_dev_num, warp_ip_udp_buffer * arp_request) {
491
492    u32                      i;
493    int                      status;
494   
495    arp_ipv4_packet        * request        = (arp_ipv4_packet *) arp_request->offset;
496    u8                     * eth_ip_addr    = eth_device[eth_dev_num].ip_addr;
497    u8                     * eth_hw_addr    = eth_device[eth_dev_num].hw_addr;
498   
499    u32                      arp_size       = ETH_HEADER_LEN + ARP_IPV4_PACKET_LEN;
500
501    warp_ip_udp_buffer     * send_buffer    = NULL;
502    arp_ipv4_packet        * arp_reply;
503
504    // Allocate a send buffer from the library
505    send_buffer = socket_alloc_send_buffer();
506
507    // If the packet was successfully allocated
508    if (send_buffer != NULL) {
509   
510        // Initialize the send buffer
511        send_buffer->size    = arp_size;
512        send_buffer->length  = arp_size;
513       
514        // Initialize the Ethernet header
515        //     NOTE:  We will not use a UDP socket to send this packet, since this reply occurs at a
516        //            lower level in the protocol stack than a UDP socket.
517        //
518        eth_init_header((ethernet_header *)(send_buffer->data), eth_hw_addr);
519       
520        // Update the offset / length of the buffer
521        send_buffer->offset += ETH_HEADER_LEN;
522        send_buffer->length -= ETH_HEADER_LEN;
523       
524        // Get the pointer to the ARP reply packet
525        arp_reply  = (arp_ipv4_packet *) send_buffer->offset;   
526
527        // Populate the ARP reply
528        arp_reply->htype     = Xil_Htons(ARP_HTYPE_ETH);
529        arp_reply->ptype     = Xil_Htons(ETHERTYPE_IP_V4);
530        arp_reply->hlen      = ETH_MAC_ADDR_LEN;
531        arp_reply->plen      = IP_ADDR_LEN;
532        arp_reply->oper      = Xil_Htons(ARP_REPLY);
533
534        for (i = 0; i < ETH_MAC_ADDR_LEN; i++) {
535            arp_reply->sender_haddr[i] = eth_hw_addr[i];
536            arp_reply->target_haddr[i] = request->sender_haddr[i];
537        }
538     
539        for (i = 0; i < IP_ADDR_LEN ; i++) {
540            arp_reply->sender_paddr[i] = eth_ip_addr[i];
541            arp_reply->target_paddr[i] = request->sender_paddr[i];
542        }
543
544        // Update the Ethernet header
545        //     NOTE:  dest_hw_addr must be big-endian; ethertype must be little-endian
546        //
547        eth_update_header((ethernet_header *)(send_buffer->data), request->sender_haddr, ETHERTYPE_ARP);
548       
549        // Send the packet not using a socket
550        status = eth_send_frame(eth_dev_num, NULL, &send_buffer, 0x1, 0x0);
551       
552        if (status != ETH_MIN_FRAME_LEN) {
553            xil_printf("ERROR:  Issue sending ARP reply.  %d bytes sent.\n", status);
554        }
555
556        // Free the send buffer
557        socket_free_send_buffer(send_buffer);
558       
559    } else {
560        xil_printf("ERROR:  Could not allocate send buffer for ARP reply.\n");
561    }
562}
563
564
565
566/*****************************************************************************/
567/**
568 * Send an ARP Request
569 *
570 * @param   eth_dev_num  - Ethernet device on which to send ARP request
571 * @param   target_haddr - Target HW address for ARP
572 * @param   target_paddr - Target Protocol (IP) address for ARP
573 *
574 * @return  None
575 *
576 * @note    This function assumes that both socket and buffer are valid.
577 *
578 *****************************************************************************/
579void arp_request(u32 eth_dev_num, u8 * target_haddr, u8 * target_paddr) {
580
581    u32                      i;
582   
583    u8                     * eth_ip_addr    = eth_device[eth_dev_num].ip_addr;
584    u8                     * eth_hw_addr    = eth_device[eth_dev_num].hw_addr;
585   
586    u32                      arp_size       = ETH_HEADER_LEN + ARP_IPV4_PACKET_LEN;
587
588    warp_ip_udp_buffer     * send_buffer    = NULL;
589    arp_ipv4_packet        * arp_reply;
590    ethernet_header        * eth_header;
591   
592    // Allocate a send buffer
593    send_buffer = socket_alloc_send_buffer();
594
595    // If the packet was successfully allocated   
596    if (send_buffer != NULL) {
597   
598        // Initialize the send buffer
599        send_buffer->size    = arp_size;
600        send_buffer->length  = arp_size;
601       
602        // Construct the Ethernet header
603        eth_header = (ethernet_header *)(send_buffer->offset);
604       
605        for (i = 0; i < ETH_MAC_ADDR_LEN; i++) {
606            eth_header->dest_mac_addr[i] = 0xFF;
607            eth_header->src_mac_addr[i]  = eth_hw_addr[i];
608        }
609       
610        eth_header->ethertype = Xil_Htons(ETHERTYPE_ARP);
611       
612        // Update the offset / length of the buffer
613        send_buffer->offset += ETH_HEADER_LEN;
614        send_buffer->length -= ETH_HEADER_LEN;
615       
616        // Get the pointer to the ARP reply packet
617        arp_reply  = (arp_ipv4_packet *) send_buffer->offset;
618
619        // Populate the ARP request
620        arp_reply->htype     = Xil_Htons(ARP_HTYPE_ETH);
621        arp_reply->ptype     = Xil_Htons(ETHERTYPE_IP_V4);
622        arp_reply->hlen      = ETH_MAC_ADDR_LEN;
623        arp_reply->plen      = IP_ADDR_LEN;
624        arp_reply->oper      = Xil_Htons(ARP_REQUEST);
625
626        for (i = 0; i < ETH_MAC_ADDR_LEN; i++) {
627            arp_reply->sender_haddr[i] = eth_hw_addr[i];
628            arp_reply->target_haddr[i] = target_haddr[i];
629        }
630     
631        for (i = 0; i < IP_ADDR_LEN ; i++) {
632            arp_reply->sender_paddr[i] = eth_ip_addr[i];
633            arp_reply->target_paddr[i] = target_paddr[i];
634        }
635
636        // Send the packet not using a socket
637        eth_send_frame(eth_dev_num, NULL, &send_buffer, 0x1, 0x0);
638
639        // Free the send buffer
640        socket_free_send_buffer(send_buffer);
641       
642    } else {
643        xil_printf("ERROR:  Could not allocate send buffer for ARP request.\n");
644    }
645}
646
647
648
649void arp_send_announcement(u32 eth_dev_num) {
650   
651    // See http://en.wikipedia.org/wiki/Address_Resolution_Protocol#ARP_announcements
652    // for information about ARP announcements.  This implements the following ARP announcement:
653    //     - ARP Request and target_paddr == sender_paddr and target_haddr == {0, 0, 0, 0, 0, 0}
654    //
655   
656    // Hardware address should be set to all zeros
657    u8   haddr[ETH_MAC_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
658   
659    // Protocol address should be the current IP address
660    u8 * paddr                   = eth_device[eth_dev_num].ip_addr;
661
662    arp_request(eth_dev_num, haddr, paddr);
663}
664
665
666
667/*****************************************************************************/
668/**
669 * Get the Hardware address associated with the Ethernet device / IP address from
670 * the ARP cache.
671 *
672 * @param   eth_dev_num  - Ethernet device to match in the cache
673 * @param   hw_addr      - Hardware address (to be returned from the cache)
674 * @param   ip_addr      - IP address to match in the cache
675 *
676 * @return  int          - Status of the command:
677 *                             XST_SUCCESS - Command completed successfully
678 *                             XST_FAILURE - There was an error in the command
679 *
680 * @note    The reason for the "strange" order of the arguments is to maintain
681 *          consistency when specifying HW address and IP address (ie all functions
682 *          required HW address then IP address when both are part of the arguments).
683 *          Since both addresses are (u8 *), the compiler cannot tell them apart
684 *          which makes it easy to get them reversed.
685 *
686 *****************************************************************************/
687int arp_get_hw_addr(u32 eth_dev_num, u8 * hw_addr, u8 * ip_addr) {
688
689    int                      i, j;
690
691    // Look through the ARP table
692    for (i = 0; i < WARP_IP_UDP_NUM_ARP_ENTRIES; i++) {
693   
694        // If an entry is in use, then check the IP address
695        if (ETH_arp_cache[i].state == ARP_TABLE_USED) {
696       
697            // If the IP address / eth_dev_num matches, then copy the hardware address
698            if ((ETH_arp_cache[i].paddr[0] == ip_addr[0]) && 
699                (ETH_arp_cache[i].paddr[1] == ip_addr[1]) && 
700                (ETH_arp_cache[i].paddr[2] == ip_addr[2]) && 
701                (ETH_arp_cache[i].paddr[3] == ip_addr[3]) &&
702                (ETH_arp_cache[i].eth_dev_num == eth_dev_num)) {
703               
704                // Copy the hardware address
705                for (j = 0; j < ETH_MAC_ADDR_LEN; j++) {
706                    hw_addr[j] = ETH_arp_cache[i].haddr[j];
707                }
708               
709                return XST_SUCCESS;
710            }
711        }
712    }
713
714    return XST_FAILURE;
715}
716
717
718
719/*****************************************************************************/
720/**
721 * Update the ARP cache
722 *
723 * This cache uses Ethernet device and IP address as keys to index hardware addresses. 
724 *
725 * @param   eth_dev_num  - Ethernet device
726 * @param   hw_addr      - Hardware address
727 * @param   ip_addr      - IP address
728 *
729 * @return  int          - Status of the command:
730 *                             XST_SUCCESS - Command completed successfully
731 *                             XST_FAILURE - There was an error in the command
732 *
733 * @note    This function assumes that both socket and buffer are valid.
734 *
735 *****************************************************************************/
736int arp_update_cache(u32 eth_dev_num, u8 * hw_addr, u8 * ip_addr) {
737
738    int                      i, j;
739    int                      first_unused_entry = -1;
740    int                      oldest_entry       = -1;
741    int                      entry_age          = -1;
742    int                      entry_to_use       = -1;
743   
744    // Look through the ARP table:
745    //     - Check that current IP address to see if entry already exists for the Ethernet device;
746    //       - Update the hw address
747    //       - Set age to zero
748    //     - Update the age of all entries that are being used
749    //     - Record the first unused entry
750    //     - Record the oldest entry
751    //
752    for (i = 0; i < WARP_IP_UDP_NUM_ARP_ENTRIES; i++) {
753        if (ETH_arp_cache[i].state == ARP_TABLE_USED) {
754
755            // If this entry is older than the current oldest, then record it and update the age
756            if (entry_age < ETH_arp_cache[i].age) {
757                oldest_entry = i;
758                entry_age    = ETH_arp_cache[i].age;
759            }
760       
761            // Update the age of the used entry       
762            ETH_arp_cache[i].age += 1;
763
764            // If the IP address / eth_dev_num matches, then copy the hardware address
765            if ((ETH_arp_cache[i].paddr[0] == ip_addr[0]) &&
766                (ETH_arp_cache[i].paddr[1] == ip_addr[1]) &&
767                (ETH_arp_cache[i].paddr[2] == ip_addr[2]) &&
768                (ETH_arp_cache[i].paddr[3] == ip_addr[3]) &&
769                (ETH_arp_cache[i].eth_dev_num == eth_dev_num)) {
770       
771                // Copy the hardware address
772                for (j = 0; j < ETH_MAC_ADDR_LEN; j++) {
773                    ETH_arp_cache[i].haddr[j] = hw_addr[j];
774                }
775
776                // Set age to zero
777                ETH_arp_cache[i].age = 0;
778               
779                // We are done updating the table
780                return XST_SUCCESS;
781            }
782       
783        } else {
784            // Record first unused entry
785            if (first_unused_entry < 0) {
786                first_unused_entry = i;
787            }
788        }
789    }   
790
791    // If we reach, here we need to add the entry to the table
792    //     - If there is an unused entry, then we should use that
793    //     - If there are no unused entries, then we should use the oldest entry (LRU replacement policy)
794    //
795    if (first_unused_entry != -1) {
796        entry_to_use = first_unused_entry;
797       
798        // Mark unused entry as used
799        ETH_arp_cache[entry_to_use].state = ARP_TABLE_USED;
800    } else {
801        entry_to_use = oldest_entry;
802    }
803
804    // Copy the IP / HW addresses and eth_dev_num to entry
805    if (entry_to_use != -1) {
806        // Copy IP address
807        for (i = 0; i < IP_ADDR_LEN; i++) {
808            ETH_arp_cache[entry_to_use].paddr[i] = ip_addr[i];
809        }
810   
811        // Copy HW address   
812        for (i = 0; i < ETH_MAC_ADDR_LEN; i++) {
813            ETH_arp_cache[entry_to_use].haddr[i] = hw_addr[i];
814        }
815       
816        // Copy Ethernet device
817        ETH_arp_cache[entry_to_use].eth_dev_num = eth_dev_num;
818   
819    } else {
820        return XST_FAILURE;
821    }
822
823    return XST_SUCCESS;
824}
825
826
827
828
829/**********************************************************************************************************************/
830/**
831 * @brief IMCP Functions
832 *
833 **********************************************************************************************************************/
834
835
836/*****************************************************************************/
837/**
838 * Process the IMCP packet
839 *
840 * @param   eth_dev_num - Ethernet device the message came in on
841 * @param   packet      - WARP IP/UDP Buffer containing the IMCP packet
842 *
843 * @return  int         - Always returns 0 since we don't want higher level transports
844 *                        to process this packet
845 *
846 * @note    The library only support Echo Request IMCP packets
847 * @note    This function assumes that both Ethernet device and buffer are valid.
848 *
849 *****************************************************************************/
850int imcp_process_packet(u32 eth_dev_num, warp_ip_udp_buffer * packet) {
851
852    imcp_header  * imcp = (imcp_header *) packet->offset;
853   
854    // Check if this is an IMCP Echo Request to the node
855    if ((imcp->type == ICMP_ECHO_REQUEST_TYPE) && (imcp->code == ICMP_ECHO_CODE)  ) {
856        // Send an IMCP Echo Reply
857        imcp_echo_reply(eth_dev_num, packet);
858    }
859
860    return 0;    // Upper layer stacks should not process this packet so return zero bytes
861}
862
863
864
865/*****************************************************************************/
866/**
867 * Send an IMCP Echo Reply
868 *
869 * @param   eth_dev_num      - Ethernet device the message came in on
870 * @param   echo_request     - WARP IP/UDP Buffer containing the IMCP Echo Request
871 *
872 * @return  None
873 *
874 * @note    This function assumes that both socket and buffer are valid.
875 *
876 *****************************************************************************/
877void imcp_echo_reply(u32 eth_dev_num, warp_ip_udp_buffer * echo_request) {
878
879    u32                      i;
880   
881    u8                     * eth_ip_addr         = eth_device[eth_dev_num].ip_addr;
882    u8                     * eth_hw_addr         = eth_device[eth_dev_num].hw_addr;
883   
884    // De-construct the input Echo Request
885    //   NOTE:  Function expects that the echo_request->offset is pointing to the IMCP header
886    //          and that the buffer contains the entire received packet
887    //
888    imcp_echo_header       * recv_imcp_hdr       = (imcp_echo_header *) echo_request->offset;
889    u8                     * recv_imcp_data      = (u8 *)(echo_request->offset + IMCP_HEADER_LEN);
890    u32                      recv_imcp_data_len  = echo_request->length - IMCP_HEADER_LEN;
891    ipv4_header            * recv_ip_hdr         = (ipv4_header *)(echo_request->offset - IP_HEADER_LEN_BYTES);
892    ethernet_header        * recv_eth_hdr        = (ethernet_header *)(echo_request->offset - IP_HEADER_LEN_BYTES - ETH_HEADER_LEN);
893   
894    warp_ip_udp_buffer     * send_buffer         = NULL;
895    imcp_echo_header       * send_imcp_hdr;
896
897   
898    // Allocate the send buffer
899    send_buffer = socket_alloc_send_buffer();
900
901    // If the packet was successfully allocated   
902    if (send_buffer != NULL) {
903   
904        // Initialize the send buffer
905        send_buffer->size    = echo_request->size;          // Size of reply is the same as the request
906        send_buffer->length  = echo_request->size;
907
908        // Initialize the Ethernet header
909        //     NOTE:  We will not use a UDP socket to send this packet, since this reply occurs at a
910        //            lower level in the protocol stack than a UDP socket.
911        //
912        eth_init_header((ethernet_header *)(send_buffer->offset), eth_hw_addr);
913       
914        // Update the offset / length of the buffer
915        send_buffer->offset += ETH_HEADER_LEN;
916        send_buffer->length -= ETH_HEADER_LEN;
917
918        // Initialize the IP header
919        ipv4_init_header((ipv4_header *)(send_buffer->offset), eth_ip_addr);
920       
921        // Update the offset / length of the buffer
922        send_buffer->offset += IP_HEADER_LEN_BYTES;
923        send_buffer->length -= IP_HEADER_LEN_BYTES;
924       
925        // Get the pointer to the IMCP reply header
926        send_imcp_hdr = (imcp_echo_header *)(send_buffer->offset);
927
928        // Populate the IMCP reply
929        send_imcp_hdr->type       = ICMP_ECHO_REPLY_TYPE;
930        send_imcp_hdr->code       = ICMP_ECHO_CODE;
931        send_imcp_hdr->checksum   = 0;
932        send_imcp_hdr->identifier = recv_imcp_hdr->identifier;
933        send_imcp_hdr->seq_num    = recv_imcp_hdr->seq_num;
934
935        // Update the offset / length of the buffer
936        send_buffer->offset += IMCP_HEADER_LEN;
937        send_buffer->length -= IMCP_HEADER_LEN;
938       
939        // Copy all the data from the request packet
940        for (i = 0; i < recv_imcp_data_len; i++) {
941            send_buffer->offset[i] = recv_imcp_data[i];
942        }
943
944        // Update the buffer to include the IMCP header because checksum has to be
945        // calculated over IMCP header and payload
946        send_buffer->offset -= IMCP_HEADER_LEN;
947        send_buffer->length += IMCP_HEADER_LEN;
948       
949        // Calculate the ICMP checksum
950        send_imcp_hdr->checksum   = Xil_Htons(ipv4_compute_checksum(send_buffer->offset, send_buffer->length));
951       
952        // Update the buffer to include the IP header
953        send_buffer->offset -= IP_HEADER_LEN_BYTES;
954        send_buffer->length += IP_HEADER_LEN_BYTES;
955       
956        // Update the IP header
957        //     NOTE:  Requires dest_ip_addr to be big-endian; ip_length to be little-endian
958        //
959        ipv4_update_header((ipv4_header *)send_buffer->offset, recv_ip_hdr->src_ip_addr, send_buffer->length, IP_PROTOCOL_IMCP);
960       
961        // Update the Ethernet header
962        //     NOTE:  dest_hw_addr must be big-endian; ethertype must be little-endian
963        //
964        eth_update_header((ethernet_header *)send_buffer->data, recv_eth_hdr->src_mac_addr, ETHERTYPE_IP_V4);
965       
966        // Send the packet not using a socket
967        eth_send_frame(eth_dev_num, NULL, &send_buffer, 0x1, 0x0);
968       
969        // Free the send buffer
970        socket_free_send_buffer(send_buffer);
971
972    } else {
973        xil_printf("ERROR:  Could not allocate send buffer for IMCP request.\n");
974    }
975}
976
977
978
Note: See TracBrowser for help on using the repository browser.