////////////////////////////////////////////////////////////////////////////// // INTEL CORPORATION PROPRIETARY INFORMATION // This software is supplied under the terms of a license agreement or // nondisclosure agreement with Intel Corporation and may not be copied // or disclosed except in accordance with the terms of that agreement. // Copyright (c) 2000 Intel Corporation. All Rights Reserved. ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // IPv4UnicastFwd - The initialization and transform block for the // IPv4 unicast forwarder (near-RFC 1812 compliant L3 // forwarder) // // Description: // This file contains the IPv4UnicastFwd_Init and IPv4UnicastFwd // macros that collectively implement the IPv4 unicast forwarding // microblock // // Contents: // IPv4UnicastFwd_Init - One-time initialization for the // forwarder. Must be called once, // and only once, before any calls // to IPv4UnicastFwd are made. // IPv4UnicastFwd - The actual forwarding operation. // This transform block is assumed // to only be invoked with packets // that have a proper (i.e, local) // source mac address. It will validate // the IP packet and perform the forwarding // operation. // #ifndef _IPV4UNICASTFWD_ #define _IPV4UNICASTFWD_ #include "stdmac.uc" #include "field.uc" #include "xbuf.uc" #include "endian.uc" #include "ip.uc" #include "Buf_h.uc" #include "IPv4UnicastFwdControlBlock.h" #include "rtm/NextHopInfo.h" #include "rtm/RTMCommon.h" #include "IPv4UnicastFwd-internal_h.uc" #include "Debug_h.uc" ////////////////////////////////////////////////////////////////////////////// // IMPORTED VARIABLES (static control block) ////////////////////////////////////////////////////////////////////////////// #include "IPv4UnicastFwdImportVars.h" ////////////////////////////////////////////////////////////////////////////// // RETURN VALUES ////////////////////////////////////////////////////////////////////////////// #define IPV4UNICASTFWD_PASS 1 #define IPV4UNICASTFWD_DROP 2 ////////////////////////////////////////////////////////////////////////////// // PUBLIC INTERFACE ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Function: IPv4UnicastFwd_Init // // Parameters: // None // // Description: // This routine performs the one-time initialization for the IPv4UnicastFwd // transform block. It must be called only once and before IPv4UnicastFwd // is invoked for the first time. // // Returns: // N/A // // Context: // // Comments: #macro IPv4UnicastFwd_Init() ; #macro IPv4UnicastFwd_Init() ; EMPTY #endm ////////////////////////////////////////////////////////////////////////////// // Function: IPv4UnicastFwd // // Parameters: // None // // Description: // This routine performs the actual forwarding operation. This transform // block will take the current buffer, verify it is an IP packet, and // attempt to perform a longest-prefix match on the IP destination. The // given packet is assumed to be properly targeted at L2. That is, the // dmac is assumed to be local. // // Returns: // This block can throw many exceptions which are handled by the L3Fwdr // in the core. These exceptions are for packets: // - with IP options // - with multicast destination addresses, // - with any errors requiring ICMP responses // - without an ARP entry // If an exception is not thrown, either IPV4UNICAST_DROP or // IPV4UNICAST_PASS will be returned as the next block in the dispatch // loop. If the output is IPV4UNICAST_PASS, the outgoing interface dispatch // loop variable will be set based on the LPM and the packet will have // the DMAC of the next hop IP address properly patched. // // Context: // // Comments: #macro IPv4UnicastFwd() ; #macro IPv4UnicastFwd() .local data_ptr interface_mask dbcast_table stats_addr ; Get a pointer to the packet data. We don't handle the offset ; correctly so the packet MUST be word aligned within the buffer. .local data_offset Buf_GetData(data_ptr, dl_buffer_handle) DL_GetBufferOffset(data_offset) alu[data_ptr, data_ptr, +, data_offset, >>3] Debug_Assert(data_offset & 0x7 == 0) .endlocal ; Read the last 6 bytes of the ethernet header ; and the first 26 bytes of the IP header. This is because SDRAM reads ; are of an 8 byte granularity. ; FHC: The name "ip_header" is a bit of a misnomer. (ps. an ethernet header is 14 bytes) ; FHC: 6 bytes dest MAC, 6 bytes src MAC, 2 bytes type ; FHC: Keep in mind that $$ip_header is really 16 independant registers ; FHC: - 8 32-bit sdram read transfer registers ; FHC: - 8 32-bit sdram write transfer registers ; FHC: TCP/IP fields are not long-word aligned anymore because the ethernet header is 14 bytes ; FHC: (not a multiple of 4) ; FHC: Also, read and write registers do not share the same indexing xbuf_alloc($$ip_header, 8) sdram[read, $$ip_header[0], data_ptr, 1, 4], sig_done // BEGIN A CRITICAL SECTION ON SDRAM SIGNALS ; Read in our control block values ; FHC: Defined in this file - This routine reads in various pieces of the control block (both static ; FHC: and dynamic) into registers. _IPv4UnicastFwd_SetupControlBlockVals(interface_mask, dbcast_table, stats_addr) ; FHC: /opt/ixasdk/src/microace/aces/l3forward_ace/include/IPv4UnicastFwd-internal_h.uc ; FHC: This macro will increment a packet counter in the statistics portion of the L3 Control Block. ; FHC: This counter incremented is based on the current dispatch loop variable for the input port. _IPv4UnicastFwd_IncrementPacketCounter(stats_addr, IPV4_UCODESTATS_INCOMING_DGRAMS) ctx_arb[sdram] ; wait for the read of the IP header // END A CRITICAL SECTION ON SDRAM SIGNALS ; Check the type of packet, if ARP, send to the core, if not IP, drop .local type expected_type xbuf_extract(type, $$ip_header, 4, 0, 2) immed[expected_type, ETH_TYPE_IP] alu[--, type, -, expected_type] br=0[ip_packet#], guess_branch ; .if (type == ETH_TYPE_ARP) ; br[exception#] immed32(expected_type, ETH_TYPE_ARP) alu[--, type, -, expected_type] br=0[exception_arp#] br[drop#] ; .endif .endlocal ip_packet#: ; Validate the packet ; FHC: This macro will verify an IP header to make sure that it's version, length and checksum are valid. ; FHC: from /microace/aces/l3forward_ace/include/IPv4UnicastFwd-internal_h.u _IPv4UnicastFwd_IPHeaderValidate(dl_next_block, dl_buffer_handle, $$ip_header, stats_addr) br!=0[end#] .local sip xbuf_extract(sip, $$ip_header, 6, IP_SOURCE_ADDRESS) ; FHC: This macro will verify an source IP address to make sure that its addresses ; FHC: are valid (not class E, multicast, 127.x.x.x, or zero) ; FHC: from /microace/aces/l3forward_ace/include/IPv4UnicastFwd-internal_h.u _IPv4UnicastFwd_IPAddressValidate(dl_next_block, dl_buffer_handle, sip, stats_addr) br!=0[end#] ; FHC: This macro will verify the given IP address does not contain martian or ; FHC: invalid addresses w.r.t. our list of interface's IP addresses/subnets. ; FHC: from /microace/aces/l3forward_ace/include/IPv4UnicastFwd-internal_h.u _IPv4UnicastFwd_IPAddressPrefixValidate(dl_next_block, dl_buffer_handle, sip, dbcast_table, interface_mask, stats_addr) br=0[end#] .endlocal ; Perform LPM .local inport outport route_entry .local ip_dest_addr first_trie trie_base DL_GetInputPort(inport) immed32(first_trie, _IPV4UNICASTFWD_FIRST_TRIE) immed32(trie_base, _IPV4UNICASTFWD_TRIE_BASE) _IPv4UnicastFwd_IncrementPacketCounter[stats_addr, IPV4_UCODESTATS_FWDING_ATTEMPT] xbuf_extract(ip_dest_addr, $$ip_header, 6, IP_DESTINATION_ADDRESS) _IPv4UnicastFwd_Lookup(route_entry, ip_dest_addr, trie_base, first_trie) .endlocal // ip_dest_addr first_trie trie_base ; Decrement TTL and check for discard .local mask temp temp2 ip_ttl ip_cksum sum ; TTL decrement xbuf_extract(ip_ttl, $$ip_header, 6, IP_TIME_TO_LIVE) alu[ip_ttl, ip_ttl, -, 1] br<=0[exception#] .local ttl_word move(ttl_word, $$ip_header[3]) ld_field[ttl_word, 0010, ip_ttl, <<8] move($$ip_header[5], ttl_word) .endlocal ; FHC: This code is a little bit goofy to read. They read and write SDRAM transfer registers are ; FHC: indexed differently. The first quad-word of the SDRAM write transfer registers stores ; FHC: the packet starting at the ethernet dest MAC (beginning of packet). ; FHC Note: (0x000000ff & $$ip_header[5]) -> $$ip_header[7] is (dest ip & 0x00ff0000) ; FHC Note: (0x0000ff00 & $$ip_header[5]) -> $$ip_header[7] is (dest ip & 0xff000000) ; FHC Note: (0xff000000 & $$ip_header[5]) -> $$ip_header[7] is (src ip & 0x0000ff00) ; move ip header to write side of transfer regs move($$ip_header[3], $$ip_header[1]) move($$ip_header[4], $$ip_header[2]) // word 3 is moved when ttl is calculated // word 4 is moved when checksum is calculated move($$ip_header[7], $$ip_header[5]) .local cksum_word ; Extract checksum xbuf_extract(ip_cksum, $$ip_header, 6, IP_CHECKSUM) ; This word will be used later to place the checksum result ; back into the IP header. Need to save it now so that it ; does not get overwritten when the route entry is read ; FHC: temp2's first 2 bytes are the checksum, and the last 2 are the first 2 bytes of the ; FHC: source ip move[temp2, $$ip_header[4]] ; Read route entry .local route_base immed32(route_base, _IPV4UNICASTFWD_ROUTE_BASE) alu[route_entry, route_base, +, route_entry, >>1] .endlocal ; FHC: RTM_NEXTHOPINFO_NUM_QWORDS_TO_READ is 3, defined in NextHopInfo.h ; FHC: In this portion of the code, the ip_header read transfer register array is ; FHC: being used as a buffer - the name ip_header is not meaningful in this context at all sdram[read, $$ip_header[0], route_entry, 0, RTM_NEXTHOPINFO_NUM_QWORDS_TO_READ], sig_done // START OF A CRITICAL SECTION, DO NOT ADD ANY SDRAM // REFERENCES, OR BRANCHES ; Recalculate checksum ; FHC: This is fast non-portable way of accounting for TTL-- ; FHC: I believe this assumes big-endian. ; FHC: See RFC 1141 for an explanation of the quick checksum recomputing ; FHC: RFC 1071 defines the internet checksum ; FHC: /microace/include/IXPblocks/ip.uc has some code that may be useful to recalculate cksum ; FHC: A bit goofy implementation because of the A/B register nonsense, ; FHC: but whatever.... immed[temp, 0x100] ; FHC temp=0x100 alu[sum, ip_cksum, +, temp] ; FHC sum = ip_cksum + 0x100 move(temp, sum) ; FHC temp = ip_cksum + 0x100 alu[ip_cksum, temp, +, sum, >>16] ; FHC ip_cksum = temp + (sum>>16) (add carry bit) ld_field[temp2, 1100, ip_cksum, <<16] ; FHC load upper 2 bytes of temp2 with ip_cksum move[$$ip_header[6], temp2] ; FHC and write it back to the header .endlocal // cksum_word .endlocal // mask temp temp2 ip_ttl ip_cksum sum ctx_arb[sdram] // END OF CRITICAL SECTION ; FHC NextHopInfo.h defines RTM_NEXTHOPINFO_ITF to be 0 move(outport, $$ip_header[RTM_NEXTHOPINFO_ITF]) ; Check for a missing route alu[--, outport, +, 1] br=0[exception#] ; Check redirect ; Dispatch to core alu[--, inport, -, outport] br=0[exception#] ; Check the L2 flags .local rx_flags DL_GetRxStat(rx_flags) br_bset[rx_flags, IX_RXSTAT_BCAST, drop#] br_bset[rx_flags, IX_RXSTAT_MCAST, drop#] .endlocal // rx_flags ; Check for no valid DMAC ; FHC NextHopInfo.h defines RTM_NEXTHOPINFO_FLAGS to be 5 br_bclr[$$ip_header[RTM_NEXTHOPINFO_FLAGS], RTF_DMAC_VALID_BIT, exception#] ; Read so we can check if the ingress and ; egress interfaces are up and that no fragmentation is needed .local total_len ; Check the MTU DL_GetBufferLength(total_len) alu[total_len, total_len, -, ETH_HDR_LEN] ; FHC: NextHopInfo.h defines RTM_NEXTHOPINFO_FLAGS to be 4 alu[--, $$ip_header[RTM_NEXTHOPINFO_MTU], -, total_len] br<0[exception#] .endlocal // total_len ; Check if ingress is disabled ; Dispatch to core alu_shf_left(--, interface_mask, AND, 1, inport) br=0[exception#] check_egress#: ; Check if egress is disabled ; Dispatch to core alu_shf_left(--, interface_mask, AND, 1, outport) br=0[exception#] ; need to move the l2 header info into sdram transfer regs and ; start the write into memory. Even we encounter an error, ; RFC1812 says it is okay that we have decremented the TTL ; FHC: NextHopInfo.h defines RTM_NEXTHOPINFO_MACADDRS_* to be 1, 2 and 3 ; FHC: What we're doing here is setting the new destination mac address move($$ip_header[0], $$ip_header[RTM_NEXTHOPINFO_MACADDRS_START]) move($$ip_header[1], $$ip_header[RTM_NEXTHOPINFO_MACADDRS_MIDDLE]) move($$ip_header[2], $$ip_header[RTM_NEXTHOPINFO_MACADDRS_END]) ; FHC this line is overkill - we're writing back more bytes than we need to sdram[write, $$ip_header[0], data_ptr, 0, 4], ctx_swap DL_SetOutputPort(outport) .endlocal // inport outport route_entry pass#: ; We passed immed32[dl_next_block, IPV4UNICASTFWD_PASS] br[end#] drop#: immed32[dl_next_block, IPV4UNICASTFWD_DROP] br[end#] .local exception_code exception_arp#: immed32[exception_code, IPV4UNICASTFWD_EXCEPTION_ARP] br[set_exception_code#] exception#: immed32[exception_code, IPV4UNICASTFWD_EXCEPTION_SLOW_PATH] br[set_exception_code#] set_exception_code#: ; FHC: dl_next_block is a register defined in the dispatch loop that marks ; FHC: where packet processing should go immed32[dl_next_block, IX_EXCEPTION] DL_SetExceptionCode(exception_code) .endlocal // exception_code end#: xbuf_free[$$ip_header] .endlocal #endm ////////////////////////////////////////////////////////////////////////////// // PRIVATE INTERFACE ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Function: _IPv4UnicastFwd_SetupControlBlockVals // // Parameters: // out_interface_mask - The inteface mask from the control block // out_dbcast_addr - The base address of the interface configuration // information from the control block // out_stats_addr - The base address of the per interface stats // stored in the control block // // Description: // This routine reads in various pieces of the control block (both static // and dynamic) into registers. // // Returns: // See parameters // // Context: // // Comments: #macro _IPv4UnicastFwd_SetupControlBlockVals(out_interface_mask, out_dbcast_addr, out_stats_addr) ; #macro _IPv4UnicastFwd_SetupControlBlockVals( ; out_interface_mask, ; out_dbcast_addr, ; out_stats_addr) .local cntl_blk $interface_mask .xfer_order $interface_mask immed32[cntl_blk, _IPV4UNICASTFWD_CONTROL_BLOCK] immed32[out_stats_addr, _IPV4UNICASTFWD_STATS_BLOCK] ; Start the read of the interface mask sram[read, $interface_mask, cntl_blk, IPV4CONTROLBLOCK_INTERFACE_MASK, 1], ctx_swap alu[out_dbcast_addr, cntl_blk, +, IPV4CONTROLBLOCK_DBCAST_TABLE] move[out_interface_mask, $interface_mask] .endlocal #endm #endif // _IPV4UNICASTFWD_