/* NAT_pkt_handler.c - packet handler and table management functions */ #include #include /* Code uses the IXA Resource Manager */ #undef LINUX /* Prevents the compiler from complaining */ #include /* Code uses the IXA CCI */ #include #include "NAT_shared_defs.h" #include "NAT_types.h" #include "NAT_net.h" /* Macro to drop a packet and quit packet handler */ #define drop(arg_hBuffer) { ix_rm_buffer_free(arg_hBuffer);\\\\ return IX_SUCCESS; } /* Verbosity level */ extern int verb; /* Static gateway IP address */ extern unsigned int gateway_ip; /* Static network interface configuration */ extern net_if iface_table[]; /* Global NAT port */ static unsigned short global_nport=0; /* Scratch rings */ extern ix_hw_ring_handle rxToNatRing, txScrRing[]; /* List of free buffers */ extern ix_buffer_free_list_handle hwFreeList; /* Pointers to various run-time data structures */ extern nat_entry *f_nat_table, *r_nat_table; extern unsigned int *f_index,*r_index; extern arp_entry *arp_table; extern unsigned char *f_timer, *r_timer; /* Global procedures */ ix_error nat_pkt_handler(ix_buffer_handle,ix_uint32,void*); ix_error resolve_arp(unsigned int); /* Local procedures */ static void process_arp_req(arp*,ix_hw_buffer_meta*, ix_buffer_handle,eth*); static void process_arp_rep(arp*,ix_hw_buffer_meta*); static void send_icmp_echo_rep(ip*,icmp*,ix_hw_buffer_meta*, ix_buffer_handle,eth*); static int process_icmp(icmp*,nat_entry*); static int process_udp(udp*,nat_entry*); static int process_tcp(tcp*,nat_entry*); static char* find_arp_entry(unsigned int); static int add_arp_entry(arp_entry*); static int add_nat_entry(nat_entry*); static void add_r_nat_entry(unsigned int); static void del_nat_entry(unsigned int); static int set_new_nport(nat_entry*); static void send_pkt(void*, unsigned int, eth*, unsigned char *, unsigned short); /* Packet handler called when an exception packet arrives */ ix_error nat_pkt_handler( ix_buffer_handle arg_hBuffer, ix_uint32 arg_UserData, /* Exception code */ void* arg_pContext ) { ix_hw_buffer_meta *meta_data; void *buf; int cksum; eth *eth_pkt; arp *arp_pkt; arp_entry ae; ip* ip_pkt; icmp* icmp_pkt; tcp* tcp_pkt; udp* udp_pkt; nat_entry ne; char * gw_eth; ix_rm_buffer_get_data(arg_hBuffer, &buf); ix_rm_buffer_get_meta(arg_hBuffer, (void **)&meta_data); eth_pkt=(eth*)((int)buf+(int)(meta_data->m_Offset)); if (eth_pkt->e_type == ETH_ARP) { /* Received ARP */ if (verb == VERBOSE) printk("Received ARP packet\\\\n"); arp_pkt=(arp*)(eth_pkt->data); if ( arp_pkt->ar_op == ARP_REQ && arp_pkt->ar_tpa == iface_table[meta_data->m_InputPort].ip_addr) { /* Process ARP request */ process_arp_req(arp_pkt,meta_data, arg_hBuffer,eth_pkt); return IX_SUCCESS; } else if ( arp_pkt->ar_op == ARP_REP && arp_pkt->ar_tpa == iface_table[meta_data->m_InputPort].ip_addr) { /* Process an ARP reply */ process_arp_rep(arp_pkt,meta_data); return IX_SUCCESS; } else drop(arg_hBuffer); } if (eth_pkt->e_type != ETH_IP) drop(arg_hBuffer); /* Received IP packet */ ip_pkt=(ip*)(eth_pkt->data); if (verb == VERBOSE) { printk("Received IP packet\\\\n"); printk("\\\\tIP src = %i.%i.%i.%i\\\\n",IP2B(ip_pkt->ip_src)); printk("\\\\tIP dst = %i.%i.%i.%i\\\\n",IP2B(ip_pkt->ip_dst)); printk("\\\\tprotocol: %i\\\\n",ip_pkt->ip_p); printk("\\\\tingress port = %i\\\\n", meta_data->m_InputPort); } /* For simplicity, the code drops a datagram that */ /* contains IP options */ if ( ip_pkt->ip_hl > 5) drop(arg_hBuffer); if ( ip_pkt->ip_dst == iface_table[meta_data->m_InputPort].ip_addr) { /* The packet is destined to the NAT box itself */ if (ip_pkt->ip_p == IPT_ICMP) { /* Received ICMP */ icmp_pkt=(icmp*)(ip_pkt->data); if (icmp_pkt->icmp_type == ICMP_ECHO_REQ) { /* Received ping */ if (verb == VERBOSE) printk("Received ping for us\\\\n"); /* Send an echo reply */ send_icmp_echo_rep(ip_pkt,icmp_pkt, meta_data, arg_hBuffer,eth_pkt); return IX_SUCCESS; } } } if ( /* Packet not from the gateway */ meta_data->m_InputPort != NAT_IFC && /* An entry is present in ARP table for the gateway */ (gw_eth = find_arp_entry(GW_IP)) != NULL ) { /* The packet is an exception because the NAT lookup */ /* failed, so add a new entry to the NAT table */ ne.valid=0; /* Will be set to 1 later */ ne.prot=ip_pkt->ip_p; ne.ip_addr_loc=ip_pkt->ip_src; ne.ip_addr_rem=ip_pkt->ip_dst; switch (ip_pkt->ip_p) { case IPT_ICMP: /* Packet is ICMP */ icmp_pkt=(icmp*)(ip_pkt->data); if (process_icmp(icmp_pkt,&ne) < 0) drop(arg_hBuffer); break; case IPT_TCP: /* Received TCP */ tcp_pkt=(tcp*)(ip_pkt->data); if (process_tcp(tcp_pkt,&ne) < 0) drop(arg_hBuffer); break; case IPT_UDP: /* Received UDP */ udp_pkt=(udp*)(ip_pkt->data); if (process_udp(udp_pkt,&ne) < 0) drop(arg_hBuffer); break; default: drop(arg_hBuffer); } /* Create an ARP entry for this packet in case a reply */ /* comes later */ ae.ip_addr=ip_pkt->ip_src; ae.eth_w0=*(int*)eth_pkt->e_src; ae.eth_w1=(*(short*)ð_pkt->e_src[4]); ae.ifnum = meta_data->m_InputPort; ae.valid = 1; /* Update the IP checksum */ cksum = ip_pkt->ip_sum+(ip_pkt->ip_src>>16) + (ip_pkt->ip_src&0xFFFF) + ((~iface_table[NAT_IFC].ip_addr)>>16) + ((~iface_table[NAT_IFC].ip_addr)&0xFFFF); cksum=(cksum&0xFFFF)+(cksum>>16); cksum=(cksum&0xFFFF)+(cksum>>16); /* Update the IP packet */ ip_pkt->ip_src = iface_table[NAT_IFC].ip_addr; ip_pkt->ip_sum=cksum&0xFFFF; /* Transmit the packet */ send_pkt((void*)arg_hBuffer,NAT_IFC,eth_pkt, gw_eth,ETH_IP); /* Add the ARP entry that was created above */ add_arp_entry(&ae); return IX_SUCCESS; } /* Drop the packet */ drop(arg_hBuffer); } /* Function to pass packet to TX microblock */ static void send_pkt(void* buf, unsigned int ifnum, eth* eth_pkt, unsigned char * eth_addr, unsigned short eth_type) { ix_tx_req txreq; ix_uint32 txreq_size; /* Set ethernet addresses */ *(int*)eth_pkt->e_dst=*(int*)eth_addr; *(short*)((int*)eth_pkt->e_dst+1)=*(short*)((int*)eth_addr+1); *(int*)eth_pkt->e_src=iface_table[ifnum].eth_w0; *(short*)((int*)eth_pkt->e_src+1)=(iface_table[ifnum].eth_w1>>16); eth_pkt->e_type = eth_type; /* Prepare Tx request */ txreq.valid = 1; /* valid request */ txreq.reserved=0; /* reserved -- set to zero */ txreq.port = ifnum; /* outgoing interface */ txreq.buff_handle = (unsigned int)buf; /* buffer handle */ txreq_size=1; /* Put Tx request on appropriate Tx Scratch ring */ ix_rm_hw_ring_put(txScrRing[txreq.port], &txreq_size, (ix_uint32 *)&txreq); } /* ARP lookup function */ char * find_arp_entry(unsigned int ipaddr) { ix_hash_48 hash48v; int i,j; hash48v.m_LW0 = ipaddr; hash48v.m_LW1 = 0; ix_rm_hash_48_hash(&hash48v); j = hash48v.m_LW0&ARP_TABLE_BIT_MASK; for (i=0;im_Offset=0; meta_data->m_BufferSize=60; meta_data->m_PacketSize=60; eth_pkt=(eth*)((int)buf+(int)meta_data->m_Offset); arp_pkt=(arp*)(eth_pkt->data); arp_pkt->ar_hrd = 1; /* Ethernet */ arp_pkt->ar_hln = 6; arp_pkt->ar_pro = ETH_IP; /* IPv4 */ arp_pkt->ar_pln = 4; arp_pkt->ar_op = ARP_REQ; *(int*)arp_pkt->ar_tha=0; *(short*)((int*)arp_pkt->ar_tha+1)=0; arp_pkt->ar_tpa=GW_IP; *(int*)arp_pkt->ar_sha=iface_table[NAT_IFC].eth_w0; *(short*)((int*)arp_pkt->ar_sha+1)= (iface_table[NAT_IFC].eth_w1>>16); arp_pkt->ar_spa1= (short)(iface_table[NAT_IFC].ip_addr>>16); arp_pkt->ar_spa2= (short)(iface_table[NAT_IFC].ip_addr&0xFFFF); send_pkt((void*)hBuffer, NAT_IFC, eth_pkt, eth_bcast, ETH_ARP); printk("%s: Resolving gateway MAC address...\\\\n", NAT_DRIVER_NAME); ix_ossl_sleep(500); if (find_arp_entry(GW_IP) != NULL) return IX_SUCCESS; } return(-1); } /* Function to process an ARP request */ void process_arp_req(arp* arp_pkt,ix_hw_buffer_meta* meta_data, ix_buffer_handle arg_hBuffer,eth *eth_pkt) { arp_entry ae; ae.ip_addr = (arp_pkt->ar_spa1<<16)|arp_pkt->ar_spa2; ae.eth_w0 = *(int*)arp_pkt->ar_sha; ae.eth_w1 = (*(short*)((int*)arp_pkt->ar_sha+1)); ae.ifnum = meta_data->m_InputPort; ae.valid = 1; arp_pkt->ar_op = ARP_REP; *(int*)arp_pkt->ar_tha=*(int*)arp_pkt->ar_sha; *(short*)(arp_pkt->ar_tha+4)=*(short*)(arp_pkt->ar_sha+4); arp_pkt->ar_tpa=(arp_pkt->ar_spa1<<16)|arp_pkt->ar_spa2; *(int*)arp_pkt->ar_sha=iface_table[meta_data->m_InputPort].eth_w0; *(short*)(arp_pkt->ar_sha+4)= iface_table[meta_data->m_InputPort].eth_w1>>16; arp_pkt->ar_spa1=iface_table[meta_data->m_InputPort].ip_addr>>16; arp_pkt->ar_spa2= iface_table[meta_data->m_InputPort].ip_addr&0xFFFF; send_pkt((void*)arg_hBuffer, meta_data->m_InputPort, eth_pkt, eth_pkt->e_src, ETH_ARP); if (verb == VERBOSE) printk("Sent ARP reply\\\\n"); /* Also add an entry into arp table */ if ( !add_arp_entry(&ae) ) printk("%s: ARP table full!", NAT_DRIVER_NAME); } /* Function to process an ARP reply */ void process_arp_rep(arp* arp_pkt,ix_hw_buffer_meta* meta_data) { arp_entry ae; ae.ip_addr = (arp_pkt->ar_spa1<<16) | arp_pkt->ar_spa2; ae.eth_w0 = *(int*)arp_pkt->ar_sha; ae.eth_w1 = *(short*)(arp_pkt->ar_sha+4); ae.ifnum = meta_data->m_InputPort; ae.valid = 1; if ( !add_arp_entry(&ae) ) printk("%s: ARP table full!", NAT_DRIVER_NAME); } /* Function to insert an entry into the ARP table. */ /* Note: because our code uses a simplified ARP table in which entries */ /* do not expire, there is no need to check for duplicate entries. */ int add_arp_entry(arp_entry *ae) { ix_hash_48 hash48v; int i,j; hash48v.m_LW0 = ae->ip_addr; hash48v.m_LW1 = 0; ix_rm_hash_48_hash(&hash48v); j = hash48v.m_LW0&ARP_TABLE_BIT_MASK; for (i=0;iip_addr == arp_table[j].ip_addr && arp_table[j].valid ) return(1); if ( !arp_table[j].valid ) { arp_table[j]=*ae; return(1); } j=(j+1)&ARP_TABLE_BIT_MASK; } return(0); } /* Function to insert an entry into the NAT table */ int add_nat_entry(nat_entry* ne) { ix_hash_128 hash128v; unsigned char timer, del_timer; int i,j,del_cand; hash128v.m_LW0 = ne->ip_addr_rem; hash128v.m_LW1 = ne->ip_addr_loc; hash128v.m_LW2 = (ne->lport<<16)|ne->rport; hash128v.m_LW3 = ne->prot; ix_rm_hash_128_hash(&hash128v); j = (hash128v.m_LW0&NAT_TABLE_BIT_MASK)<ip_addr_loc == f_nat_table[j].ip_addr_loc && ne->ip_addr_rem == f_nat_table[j].ip_addr_rem && ne->lport == f_nat_table[j].lport && ne->rport == f_nat_table[j].rport && ne->prot == f_nat_table[j].prot ) { ne->nport=f_nat_table[j].nport; return(1); } if ( !f_nat_table[j].valid ) { if (set_new_nport(ne) < 0) return(-1); f_nat_table[j]=*ne; add_r_nat_entry(j); f_nat_table[j].valid=1; return(1); } /* No free slot was found; choose a candidate */ /* for deletion */ timer=f_timer[j]|r_timer[f_index[j]]; del_timer = f_timer[del_cand]|r_timer[f_index[del_cand]]; if ( timer < del_timer || ( timer == del_timer && f_nat_table[j].prot!=f_nat_table[del_cand].prot && ( f_nat_table[j].prot == IPT_ICMP || ( f_nat_table[j].prot == IPT_UDP && f_nat_table[del_cand].prot == IPT_TCP )))) del_cand=j; } del_nat_entry(del_cand); if (set_new_nport(ne) < 0) return(-1); f_nat_table[del_cand]=*ne; add_r_nat_entry(del_cand); f_nat_table[del_cand].valid=1; return(1); } /* Function to delete an entry from the NAT table */ void del_nat_entry(unsigned int entry_index) { f_nat_table[entry_index].valid=0; f_timer[entry_index]=0; r_nat_table[f_index[entry_index]].valid=0; r_timer[f_index[entry_index]]=0; } /* Function to add an entry to the reverse NAT table */ void add_r_nat_entry(unsigned int entry_index) { ix_hash_128 hash128v; int i, j, k, del_cand, r_del_cand; unsigned char timer, del_timer; nat_entry *ne=&f_nat_table[entry_index]; hash128v.m_LW0 = ne->ip_addr_rem; hash128v.m_LW1 = (ne->nport<<16)|ne->rport; hash128v.m_LW2 = ne->prot; hash128v.m_LW3 = 0; ix_rm_hash_128_hash(&hash128v); j=(hash128v.m_LW0&NAT_TABLE_BIT_MASK)<nport=++global_nport; /* Try at most NEW_NPORT_ATTEMPS values, and then give up */ for (i=0;iip_addr_rem; hash128v.m_LW1 = (ne->nport<<16)|ne->rport; hash128v.m_LW2 = ne->prot; hash128v.m_LW3 = 0; ix_rm_hash_128_hash(&hash128v); j=(hash128v.m_LW0&NAT_TABLE_BIT_MASK)<ip_addr_rem && r_nat_table[j].rport == ne->rport && r_nat_table[j].nport == ne->nport && r_nat_table[j].prot == ne->prot ) break; } if (k==HASH_BUCKET_SIZE) /* An unused NAT port value has been found */ return(ne->nport); /* Try the next NAT port value */ ne->nport=++global_nport; } return(-1); } /* Function to send echo response */ void send_icmp_echo_rep(ip* ip_pkt,icmp* icmp_pkt, ix_hw_buffer_meta* meta_data,ix_buffer_handle arg_hBuffer, eth *eth_pkt) { unsigned int cksum; icmp_pkt->icmp_type = ICMP_ECHO_REP; cksum = icmp_pkt->icmp_cksum+(ICMP_ECHO_REQ<<8) + ((~(ICMP_ECHO_REP<<8))&0xFFFF); cksum = (cksum&0xFFFF)+(cksum>>16); cksum = (cksum&0xFFFF)+(cksum>>16); icmp_pkt->icmp_cksum = cksum&0xFFFF; ip_pkt->ip_dst = ip_pkt->ip_src; ip_pkt->ip_src = iface_table[meta_data->m_InputPort].ip_addr; send_pkt((void*)arg_hBuffer,meta_data->m_InputPort, eth_pkt, eth_pkt->e_src, ETH_IP); } /* Function to translate ICMP packet */ int process_icmp(icmp* icmp_pkt,nat_entry* ne) { unsigned int cksum; /* If this is not an echo request -- drop the packet */ if (icmp_pkt->icmp_type != ICMP_ECHO_REQ) return(-1); /* For ICMP echo request we do ID field translation */ ne->lport=icmp_pkt->icmp_id; ne->rport=0; if (add_nat_entry(ne) < 0) return(-1); icmp_pkt->icmp_id=ne->nport; /* Update ICMP checksum */ cksum= icmp_pkt->icmp_cksum+ne->lport + ((~icmp_pkt->icmp_id)&0xFFFF); cksum=(cksum&0xFFFF)+(cksum>>16); cksum=(cksum&0xFFFF)+(cksum>>16); icmp_pkt->icmp_cksum = cksum&0xFFFF; return(1); } /* Function to translate TCP packet */ int process_tcp(tcp* tcp_pkt,nat_entry* ne) { unsigned int cksum; if (verb == VERBOSE) { printk("\\\\tTCP source port = %i\\\\n", tcp_pkt->tcp_sport); printk("\\\\tTCP dest. port = %i\\\\n", tcp_pkt->tcp_dport); } /* Perform TCP source port translation */ ne->lport=tcp_pkt->tcp_sport; ne->rport=tcp_pkt->tcp_dport; if (add_nat_entry(ne) < 0) return(-1); tcp_pkt->tcp_sport=ne->nport; /* Update the TCP checksum */ cksum = tcp_pkt->tcp_cksum+ne->lport+(ne->ip_addr_loc>>16) + (ne->ip_addr_loc&0xFFFF)+((~tcp_pkt->tcp_sport)&0xFFFF) + ((~iface_table[NAT_IFC].ip_addr)>>16) + ((~iface_table[NAT_IFC].ip_addr)&0xFFFF); cksum=(cksum&0xFFFF)+(cksum>>16); cksum=(cksum&0xFFFF)+(cksum>>16); tcp_pkt->tcp_cksum = cksum&0xFFFF; return(1); } /* Function to translate UDP packet */ int process_udp(udp* udp_pkt,nat_entry* ne) { unsigned int cksum; if (verb == VERBOSE) { printk("\\\\tUDP source port = %i\\\\n", udp_pkt->udp_sport); printk("\\\\tUDP dest. port = %i\\\\n", udp_pkt->udp_dport); } /* Perform UDP source port translation */ ne->lport=udp_pkt->udp_sport; ne->rport=udp_pkt->udp_dport; if (add_nat_entry(ne) < 0) return(-1); udp_pkt->udp_sport=ne->nport; /* Update the UDP checksum */ if (udp_pkt->udp_cksum) { cksum = udp_pkt->udp_cksum+ne->lport+(ne->ip_addr_loc>>16) + (ne->ip_addr_loc&0xFFFF) + ((~udp_pkt->udp_sport)&0xFFFF) + ((~iface_table[NAT_IFC].ip_addr)>>16) + ((~iface_table[NAT_IFC].ip_addr)&0xFFFF); cksum=(cksum&0xFFFF)+(cksum>>16); cksum=(cksum&0xFFFF)+(cksum>>16); udp_pkt->udp_cksum = cksum&0xFFFF; } return(1); }