/* NAT_microblock.uc - microcode for NAT processing */ /************************************************************************/ /* Note: this file contains code for overall NAT processing, including */ /* code to obtain incoming packets from the packet_rx[] microblock, */ /* check the packet, perform NAT processing, if needed, and forward */ /* each packet to the correct transmit queue for the sphy_mphy4_tx[] */ /* microblock. */ /************************************************************************/ #include #include #include #include #include #include /* Define NAT table location and parameters */ \\&.import_var NAT_TABLE_BASE #define_eval NAT_TABLE_BM NAT_TABLE_BIT_MASK #define_eval HASH_BUCKET_SZ HASH_BUCKET_SIZE #define_eval SHIFT_VAL 4+HASH_BUCKET_SHIFT #define_eval NAT_TABLE_SZ NAT_TABLE_SIZE /* Define ARP table location and parameters */ \\&.import_var ARP_TABLE_BASE #define_eval ARP_TABLE_BM ARP_TABLE_BIT_MASK /* Define timer table location */ \\&.import_var TIMER_TABLE_BASE /* Obtain the default gateway IP address */ \\&.import_var GATEWAY_IP_ADDR /* Obtain configurations for each network interface */ \\&.import_var IF0_IP \\&.import_var IF1_IP \\&.import_var IF0_ETH_W0 \\&.import_var IF0_ETH_W1 \\&.import_var IF1_ETH_W0 \\&.import_var IF1_ETH_W1 #define_eval NAT_IP_ADDR IF/**/NAT_IFC/**/_IP /* Define Local memory addresses */ #define STEP 64 #define LM_ADDR0_0 0 #define_eval LM_ADDR0_1 LM_ADDR0_0+STEP #define_eval LM_ADDR0_2 LM_ADDR0_1+STEP #define_eval LM_ADDR0_3 LM_ADDR0_2+STEP #define_eval LM_ADDR0_4 LM_ADDR0_3+STEP #define_eval LM_ADDR0_5 LM_ADDR0_4+STEP #define_eval LM_ADDR0_6 LM_ADDR0_5+STEP #define_eval LM_ADDR0_7 LM_ADDR0_6+STEP /*********************************/ /* Specify signals and registers */ /*********************************/ \\&.sig sig_scr_get ; Signal for scratch get \\&.sig sig_scr_put ; Signal for scratch put \\&.sig sig_pkt_hdr ; Signal for packet header read \\&.sig sig_dram_wr ; Signal for dram write done \\&.reg temp ; GPR for intermediate data \\&.reg zero ; GPR containing constant value 0 \\&.reg one ; GPR containing constant value 1 \\&.reg ring ; Scratch ring \\&.reg port ; Input port number \\&.reg $txreq ; Tx request to put on scratch rings \\&.reg eth_ipt ; GPR containing ETH_IP constant (0x0800) \\&.reg NAT_ip ; GPR containing NAT box IP address \\&.reg ctx_num ; Context number of the current thread \\&.reg if_out ; Output interface to forward packet to \\&.reg EthDstW0 EthDstW1 ; Ethernet address registers \\&.reg f_nat_table ; GPR with NAT table base \\&.reg nat_tab_bit_mask ; GPR with NAT table bit mask (size - 1) \\&.reg r_nat_table ; GPR with reverse NAT table base \\&.reg arp_tab ; GPR with ARP table base \\&.reg arp_tab_bit_mask ; GPR with ARP table bit mask (size - 1) \\&.reg f_timer ; GPR with timer table base \\&.reg r_timer ; GPR with reverse timer table base \\&.reg gateway_ip ; GPR with default gateway IP address \\&.reg nat_port ; GPR with port to substitute \\&.reg if_ip if_eth_w0 if_eth_w1 ; Network interface settings \\&.reg IpHlen IpSrc IpDst IpProt SrcPort DstPort ; flow 5-tuple /* Allocation of transfer registers */ xbuf_alloc[$$pkt_hdr,2,read_write] xbuf_alloc[$$entry_w,4,read_write] xbuf_alloc[$$iphdr,10,read_write] xbuf_alloc[$rdata, RX_TO_FUNC_MSG_SIZE, read] /*********************************/ /* Data initialization */ /*********************************/ /* Frequently used constants */ immed[zero, 0] /* 0 */ immed[one, 1] /* 1 */ immed32(eth_ipt,ETH_IP) /* Ethernet type IP */ /* Constants that are specific to NAT */ immed32(NAT_ip,NAT_IP_ADDR) immed32(f_nat_table,NAT_TABLE_BASE) immed32(nat_tab_bit_mask,NAT_TABLE_BM) immed32(arp_tab,ARP_TABLE_BASE) immed32(arp_tab_bit_mask,ARP_TABLE_BM) immed32(f_timer,TIMER_TABLE_BASE) immed32(gateway_ip,GATEWAY_IP_ADDR) #define_eval NAT_TABLE_SZ_B (NAT_TABLE_SZ<<4) immed32(temp,NAT_TABLE_SZ_B) alu[r_nat_table,f_nat_table,+,temp] immed32(temp,NAT_TABLE_SZ) alu[r_timer,f_timer,+,temp] /* Byte alignment setting */ local_csr_wr[BYTE_INDEX,2] /* Obtain the current context number */ local_csr_rd[active_ctx_sts] immed[ctx_num,0] alu[ctx_num, ctx_num, AND, 0x07] /* Set a Local memory address */ \\&.if (ctx()==0) immed[temp,LM_ADDR0_0] \\&.elif (ctx()==1) immed[temp,LM_ADDR0_1] \\&.elif (ctx()==2) immed[temp,LM_ADDR0_2] \\&.elif (ctx()==3) immed[temp,LM_ADDR0_3] \\&.elif (ctx()==4) immed[temp,LM_ADDR0_4] \\&.elif (ctx()==5) immed[temp,LM_ADDR0_5] \\&.elif (ctx()==6) immed[temp,LM_ADDR0_6] \\&.else immed[temp,LM_ADDR0_7] \\&.endif local_csr_wr[ACTIVE_LM_ADDR_0,temp] /*********************************/ /* Main loop */ /*********************************/ start#: /* Read a packet from RX scratch ring */ alu_shf[ring, --, B, PKT_RX_TO_NAT_SCR_RING, <<2] scratch[get,$rdata0,0,ring,RX_TO_FUNC_MSG_SIZE], sig_done[sig_scr_get] /* Reset the exception register */ alu[dl_exception_reg, --, b, 0] /* Wait for the RX ring read to finish */ ctx_arb[sig_scr_get] /* Check if ring is empty */ alu[--, $rdata0, -, 0] beq[ring_empty#] /* Ring is not empty */ alu[dl_buf_handle,--,b,$rdata0] /* set buffer handle */ alu[dl_eop_buf_handle, --,b,$rdata1] /* get eop parameter */ alu[dl_meta1,--,b,$rdata2] /* get data offset */ alu[port, 0xF, AND, $rdata4, >>16] /* get input port */ /* Ignore packets from ports other than 0 or 1 */ alu[--,port,-,PORTS_NUM] bge[drop#] /* Read the packet header (40 bytes) and assume Ethernet */ eth_iphdr_load(dl_buf_handle, sig_pkt_hdr) /* If frame type is not IP, send to the core */ alu[temp,--,b,$$iphdr3,>>16] alu[--,temp,xor,eth_ipt] bne[exception#], defer[2] /* defer - save some cycles here */ alu[EthDstW0,--,b,$$iphdr0] ld_field_w_clr[EthDstW1,1100,$$iphdr1] /* At this point the code has an IP packet; check the type */ alu[IpProt,0xFF,and,$$iphdr5] br=byte[IpProt,0,IPT_TCP,tcp_udp_icmp#] /* check for TCP */ br=byte[IpProt,0,IPT_UDP,tcp_udp_icmp#] /* check for UDP */ br!=byte[IpProt,0,IPT_ICMP,exception#] /* check for ICMP */ tcp_udp_icmp#: /* The packet carries TCP, UDP or ICMP */ /* Find the network interface data for the input port */ net_if_data_get(port,if_ip,if_eth_w0,if_eth_w1) /* Verify that the Ethernet destination matches our address */ alu[--,if_eth_w0,xor,EthDstW0] bne[exception#],defer[1] alu[--,if_eth_w1,xor,EthDstW1] bne[exception#],defer[2] /* Compute the IP header size */ alu[IpHlen,0xF,and,$$iphdr3,>>8] /* To simplify the code, we do not deal with IP options. */ /* If options are present, drop the packet */ alu[--,IpHlen,-,5] bgt[exception#],defer[3] /* Store a copy of the IP header in local memory */ byte_align_be[--,$$iphdr3] byte_align_be[*l$index0[0],$$iphdr4] byte_align_be[*l$index0[1],$$iphdr5] byte_align_be[*l$index0[2],$$iphdr6] byte_align_be[*l$index0[3],$$iphdr7] byte_align_be[*l$index0[4],$$iphdr8] byte_align_be[*l$index0[5],$$iphdr9] byte_align_be[*l$index0[6],0] /* Obtain the IP source and destination addresses */ alu[IpDst,--,b,*l$index0[4]] alu[--,if_ip,xor,IpDst] /* Branch if destination IP is local (i.e., the NAT box) */ beq[local_dst#],defer[1] alu[IpSrc,--,b,*l$index0[3]] /* At this point the packet contains TCP,UDP or ICMP, and has */ /* a non-local destination address. If the packet is incoming, */ /* drop it. If the packet is outgoing, perform NAT translation */ /* and send the packet to the Internet. */ alu[--,port,xor,NAT_IFC] beq[exception#] /* Read the source and destination ports (or ICMP type and ID) */ read_src_and_dst_ports(NON_LOCAL_DST,IpHlen,IpProt, SrcPort,DstPort) .if (IpProt == IPT_ICMP) /* If the packet is ICMP, but not an echo request, */ /* send the packet to the core as an exception */ alu[--,DstPort,xor,ICMP_ECHO_REQ] bne[exception#],defer[1] alu[DstPort,--,b,0] .endif /* Perfrom NAT lookup for an outgoing packet */ nat_lookup_outgoing(IpSrc,SrcPort,IpDst,DstPort,IpProt, nat_port,if_out) alu[SrcPort,--,b,nat_port] tx_pkt#: /* If NAT lookup failed, send the packet to core */ /* as an exception */ alu[--,--,~b,nat_port] beq[exception#] .set if_out /* Inserted to prevent an assembler warning */ /* At this point, NAT lookup has been successful, and the ARP */ /* table must be consulted to determine the correct Ethernet */ /* address for the frame. */ alu[dl_exception_reg, --, b, 1,<<10] arp_lookup(if_out,IpDst,EthDstW0,EthDstW1) alu[dl_exception_reg, --, b, 0] /* Modify the packet header */ modify_and_save_packet_header(if_out,EthDstW0,EthDstW1,IpHlen, IpProt,IpSrc,IpDst,SrcPort,DstPort) /* Create a TX request for transmit queue */ alu[temp, --, b, if_out, <<24] /* 27:24 output port */ ld_field[temp, 0111, dl_buf_handle] /* 23:00 buffer handle */ alu[$txreq, temp, OR, one, <<31] /* 31 valid bit */ /* bits 31:28 reserved */ /* Jump to Scratch ring write for the corresponding port */ alu[temp, --, b, if_out, <<2] jump[temp,write_ring0#],targets[write_ring0#,write_ring1#,\\\\ write_ring2#,write_ring3#] write_ring0#: write_tx_ring(0,start#) write_ring1#: write_tx_ring(1,start#) write_ring2#: write_tx_ring(2,start#) write_ring3#: write_tx_ring(3,start#) /* If Scratch ring is full -- wait voluntarily */ full_ring0#: ctx_arb[voluntary],br[write_ring0#] full_ring1#: ctx_arb[voluntary],br[write_ring1#] full_ring2#: ctx_arb[voluntary],br[write_ring2#] full_ring3#: ctx_arb[voluntary],br[write_ring3#] local_dst#: /* If the Destination IP address in an incoming datagram is */ /* not the address of the NAT box address, send the packet */ /* to the core as an exception. */ alu[--,IpDst,xor,NAT_ip] bne[exception#] /* At this point the incoming packet contains TCP, UDP, */ /* or ICMP and has a local IP destination. Read the source */ /* and destination ports. */ read_src_and_dst_ports(NON_LOCAL_SRC,IpHlen,IpProt, SrcPort,DstPort) .if (IpProt == IPT_ICMP) /* If the packet is ICMP, but not an echo reply, */ /* send the packet to the core as an exception. */ alu[--,SrcPort,xor,ICMP_ECHO_REP] bne[exception#],defer[1] alu[SrcPort,--,b,0] .endif /* Perform NAT lookup for an incoming packet */ nat_lookup_incoming(IpDst,DstPort,IpSrc,SrcPort,IpProt, nat_port,if_out) alu[DstPort,--,b,nat_port] br[tx_pkt#] /* Jump to the transmission code */ exception#: /* Send to the NAT core component */ dl_exception_set(NAT_CC_ID, 0) /* this is a packet (not message) */ dl_exception_set_priority(0) dl_exception_send(dl_buf_handle) ring_empty#: br[start#] /* Jump back to the main loop to continue probing */ drop#: /* Drop the packet by freeing its buffer */ dl_buf_free(dl_buf_handle,BUF_FREE_LIST0) br[start#] /* go back to the main loop start */