⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nat.c

📁 ecos cvs version lwip-tcpip nat addon patch.
💻 C
字号:
/*
 * Copyright (c) 2003 EISLAB, Lulea University of Technology.
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 *
 * This file is part of the NAT contribution for lwIP.
 * 
 * Author: Conny Ohult <conny@sm.luth.se>
 *
 */

/*-----------------------------------------------------------------------------------*/
/* 
 * nat.c  
 *
 *  
 */
/*-----------------------------------------------------------------------------------*/

#include "natopts.h"
#include "nat_memp.h"
#include "nat.h"

#include "lwip/inet.h"
#include "lwip/ip.h"
#include "lwip/tcp.h"
#include "lwip/udp.h"
#include "lwip/icmp.h"

#include "lwip/debug.h"

struct nat_pcb *nat_active_pcbs; /* List of all active NAT PCBs */
struct nat_pcb *nat_tmp_pcb; /* Only used for temporary storage. */

u16_t port_nxt; /* Next unique transport identifier to allocate */
u32_t ip_nxt; /* Next private IP address to allocate for a remote host */

struct nat_ctrl {
  struct netif *nat_external_netif;
  err_t (* tcpip_input)(struct pbuf *p, struct netif *inp); /* Used to pass a packet up to the 
							       TCP/IP stack */
} nat_ctrl;


/*-----------------------------------------------------------------------------------*/
/* 
 * nat_init():
 *
 * Initializes the NAT layer. Takes the lwIP external network interface and a 
 * function used to pass a packet up to lwIP as arguments.
 *
 */
/*-----------------------------------------------------------------------------------*/
void 
nat_init(struct netif *external_netif, err_t (* tcpip_input)(struct pbuf *p, struct netif *inp))
{
  /* Clear globals */
  nat_active_pcbs = NULL;
  nat_tmp_pcb = NULL;

  /* Initialize the IP address pool */
  ip_nxt = NAT_MIN_IP;

  /* Initialize the unique port number */
  port_nxt = NAT_MIN_PORT;

  /* Set network interface to the external network */
  nat_ctrl.nat_external_netif = external_netif;

  /* Set lwIP input function to be called when a packet arrives from a network interface */
  nat_ctrl.tcpip_input = tcpip_input;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * nat_tmr():
 *
 * Called every 1s and updates the idle timer attached to each session.
 * 
 */
/*-----------------------------------------------------------------------------------*/
void
nat_tmr(void) 
{
  struct nat_pcb *pcb;
  
  /* Step through all of the active pcbs */
  for(pcb = nat_active_pcbs; pcb != NULL; pcb = pcb->next) {
    ++pcb->tmr;
#if NAT_IDLE_TIMEOUT
    if(pcb->tmr >= NAT_IDLE_TIMEOUT) {
      LWIP_DEBUGF(NAT_DEBUG, ("nat_tmr: Idle timeout for session has expired and will be removed\n"));
      nat_close(pcb);
    }
#endif
#if NAT_TCP_END_DETECT
    if(pcb->state == NAT_CLOSING) {
      ++pcb->term_tmr;
      if(pcb->term_tmr >= 240) {
	LWIP_DEBUGF(NAT_DEBUG, ("nat_tmr: Session can be assumed to have been terminated\n"));
	nat_close(pcb);
      }
    }
#endif
  }
}
/*-----------------------------------------------------------------------------------*/
/* 
 * nat_next_port():
 *
 * Return a port number that can be used to form a globally unique address.
 * 
 */
/*-----------------------------------------------------------------------------------*/
u16_t
nat_next_port(void)
{
  if(port_nxt == NAT_MAX_PORT) {
    return (port_nxt = NAT_MIN_PORT);
  }
  return ++port_nxt; 
}
/*-----------------------------------------------------------------------------------*/
/* 
 * nat_new():
 *
 * 
 * 
 */
/*-----------------------------------------------------------------------------------*/
struct nat_pcb *
nat_new(struct ip_addr *ipaddr, u16_t port)
{
  struct nat_pcb *pcb, *oldest;

  pcb = nat_memp_malloc(MEMP_NAT_PCB);
  if(pcb == NULL) {
    /* Out of PCBs */
#if NAT_RETIRE_LONGEST_IDLE
    /* Retire the session that has been idle the longest time */
    LWIP_DEBUGF(NAT_DEBUG, ("nat_process: Out of PCBs. Remove the session that has been idle the longest time\n"));
    oldest = nat_active_pcbs;
    for(pcb = nat_active_pcbs->next; pcb->next != NULL; pcb = pcb->next) {
      if(pcb->tmr > oldest->tmr) {
	oldest = pcb;
      }
    }
    NAT_RMV(&(nat_active_pcbs), oldest);
    pcb = oldest;
#else
    return NULL;
#endif
  }
  pcb->local_ip.addr = ipaddr->addr;
  pcb->local_port = port;
  pcb->ass_port = htons(nat_next_port());
  pcb->tmr = 0;
#if NAT_TCP_END_DETECT
  pcb->state = NAT_OPEN;
  pcb->term_tmr = 0;
#endif
  NAT_REG(&nat_active_pcbs, pcb);

  return pcb;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * nat_close():
 *
 * 
 * 
 */
/*-----------------------------------------------------------------------------------*/
void
nat_close(struct nat_pcb *pcb)
{
  if(pcb != NULL) {
    /* Remove the NAT PCB that contain the PPP PCB from the list of active connections */
    NAT_RMV(&(nat_active_pcbs), pcb);
    nat_memp_free(MEMP_NAT_PCB, pcb);
  }
}
/*-----------------------------------------------------------------------------------*/
/* 
 * nat_chksum_adjust():
 *
 * 
 * 
 */
/*-----------------------------------------------------------------------------------*/
void 
nat_chksum_adjust(u8_t *chksum, u8_t *optr, s16_t olen, u8_t *nptr, s16_t nlen)
   /* assuming: unsigned char is 8 bits, long is 32 bits.
     - chksum points to the chksum in the packet
     - optr points to the old data in the packet
     - nptr points to the new data in the packet
   */
{
  s32_t x, old, new;
  x=chksum[0]*256+chksum[1];
  x=~x & 0xFFFF;
  while (olen) {
    old=optr[0]*256+optr[1]; optr+=2;
    x-=old & 0xffff;
    if (x<=0) {
      x--; x&=0xffff; 
    }
    olen-=2;
  }
  while (nlen) {
    new=nptr[0]*256+nptr[1]; nptr+=2;
    x+=new & 0xffff;
    if (x & 0x10000) { 
      x++; x&=0xffff; 
    }
    nlen-=2;
  }
  x=~x & 0xFFFF;
  chksum[0]=x/256; chksum[1]=x & 0xff;
  //LWIP_DEBUGF(NAT_DEBUG, ("nat_chksum_adjust: chksum = 0x%x\n", *((u16_t *)chksum)));
}
/*-----------------------------------------------------------------------------------*/
/* 
 * nat_input():
 *
 * Called by the network interface to pass a packet up to the TCP/IP stack
 * 
 */
/*-----------------------------------------------------------------------------------*/
err_t 
nat_input(struct pbuf *p, struct netif *netif)
{
  struct ip_hdr *iphdr = p->payload;
  struct tcp_hdr *tcphdr = NULL;
  struct udp_hdr *udphdr = NULL;
  struct icmp_echo_hdr *icmphdr = NULL;
  u32_t oaddr;
  u16_t dest, src;
  struct nat_pcb *pcb = NULL;
  err_t ret = ERR_OK;
  static u16_t iphdrlen;

  LWIP_DEBUGF(NAT_DEBUG, ("nat_input: iphdr->src %d.%d.%d.%d\n", (u8_t)(ntohl(iphdr->src.addr) >> 24) & 0xff, (u8_t)(ntohl(iphdr->src.addr) >> 16) & 0xff, (u8_t)(ntohl(iphdr->src.addr) >> 8) & 0xff, (u8_t)ntohl(iphdr->src.addr) & 0xff));
  LWIP_DEBUGF(NAT_DEBUG, ("nat_input: iphdr->dest %d.%d.%d.%d\n", (u8_t)(ntohl(iphdr->dest.addr) >> 24) & 0xff, (u8_t)(ntohl(iphdr->dest.addr) >> 16) & 0xff, (u8_t)(ntohl(iphdr->dest.addr) >> 8) & 0xff, (u8_t)ntohl(iphdr->dest.addr) & 0xff));

  /* Obtain IP header length in number of 32-bit words */
  iphdrlen = IPH_HL(iphdr);
  /* Calculate IP header length in bytes */
  iphdrlen *= 4;

  switch(IPH_PROTO(iphdr)) {
  case IP_PROTO_TCP:
    tcphdr = (struct tcp_hdr *)(((u8_t *)p->payload) + iphdrlen);
    dest = tcphdr->dest;
    src = tcphdr->src;
    break;
  case IP_PROTO_UDP:
    udphdr = (struct udp_hdr *)(((u8_t *)p->payload) + iphdrlen);
    dest = udphdr->dest;
    src = udphdr->src;
    break;
  case IP_PROTO_ICMP:
    icmphdr = (struct icmp_echo_hdr *)(((u8_t *)p->payload) + iphdrlen);
    src = dest = icmphdr->id;
    break;
  default:
    //TODO:
    break;
  }

  if(ntohs(dest) >= NAT_MIN_PORT && ntohs(dest) <= NAT_MAX_PORT) {
    for(pcb = nat_active_pcbs; pcb != NULL; pcb = pcb->next) {
      if(dest == pcb->ass_port) {
	break; /* Found matching connection */
      }
    }
  }
  if(pcb != NULL) {
    /* IP packet is for translated local connection */
    LWIP_DEBUGF(NAT_DEBUG, ("nat_input: IP packet is for translated local connection\n"));

    pcb->tmr = 0;/* Reset idle timer */

    /* Calculate new checksum */
    oaddr = iphdr->dest.addr;
    ((struct ip_addr *)&(iphdr->dest))->addr = pcb->local_ip.addr; 
    nat_chksum_adjust((u8_t *)&IPH_CHKSUM(iphdr),(u8_t *)&oaddr, 4, (u8_t *)&iphdr->dest.addr, 4);

    if(IPH_PROTO(iphdr) == IP_PROTO_TCP) {
      nat_chksum_adjust((u8_t *)&tcphdr->chksum,(u8_t *)&oaddr, 4, (u8_t *)&iphdr->dest.addr, 4);
      tcphdr->dest = pcb->local_port;
      nat_chksum_adjust((u8_t *)&tcphdr->chksum,(u8_t *)&dest, 2, (u8_t *)&tcphdr->dest, 2);
    } else if(IPH_PROTO(iphdr) == IP_PROTO_UDP) {
      if(udphdr->chksum != 0) {
	nat_chksum_adjust((u8_t *)&udphdr->chksum,(u8_t *)&oaddr, 4, (u8_t *)&iphdr->dest.addr, 4);
	udphdr->dest = pcb->local_port;
	nat_chksum_adjust((u8_t *)&udphdr->chksum,(u8_t *)&dest, 2, (u8_t *)&udphdr->dest, 2);
      }
    } else if(IPH_PROTO(iphdr) == IP_PROTO_ICMP && (ICMPH_CODE(icmphdr) == ICMP_ECHO || ICMPH_CODE(icmphdr) == ICMP_ER)) {
      icmphdr->id = pcb->local_port;
      nat_chksum_adjust((u8_t *)&icmphdr->chksum,(u8_t *)&dest, 2, (u8_t *)&icmphdr->id, 2);
    } else {
      return ERR_RTE;
    }   
  } else {
    /* IP packet is for AP network connection or the local TCP/IP stack */
    LWIP_DEBUGF(NAT_DEBUG, ("nat_input: IP packet is for AP network connection or the local TCP/IP stack\n"));
    for(pcb = nat_active_pcbs; pcb != NULL; pcb = pcb->next) {
      if(ip_addr_cmp(&pcb->local_ip, &iphdr->src) && pcb->local_port == src) {
	break;
      }
    }
    if(pcb == NULL) {
      /* A new session should be created if the packet came from the private network and is destined for 
	 the external network */
      if(iphdr->dest.addr != NAT_LOCAL_IP) {
	if(iphdr->src.addr >= NAT_MIN_IP && iphdr->src.addr < ip_nxt) {
	  LWIP_DEBUGF(NAT_DEBUG, ("nat_input: Create a new session\n"));
	  pcb = nat_new(&iphdr->src, src);
	}
      }
    }
    if(pcb != NULL) {
      /* IP packet is for AP network connection */
      LWIP_DEBUGF(NAT_DEBUG, ("nat_input: IP packet is for AP network connection\n"));

      if(nat_ctrl.nat_external_netif == NULL) {
	pbuf_free(p);
	return ret;
      }

      pcb->tmr = 0; /* Reset idle timer */

      /* Calculate new checksum */
      oaddr = iphdr->src.addr;
      ((struct ip_addr *)&(iphdr->src))->addr = nat_ctrl.nat_external_netif->ip_addr.addr;
      nat_chksum_adjust((u8_t *)&IPH_CHKSUM(iphdr),(u8_t *)&oaddr, 4, (u8_t *)&iphdr->src.addr, 4);
      
      switch(IPH_PROTO(iphdr)) {
      case IP_PROTO_TCP:
	nat_chksum_adjust((u8_t *)&tcphdr->chksum,(u8_t *)&oaddr, 4, (u8_t *)&iphdr->src.addr, 4);
	tcphdr->src = pcb->ass_port;
	nat_chksum_adjust((u8_t *)&tcphdr->chksum,(u8_t *)&src, 2, (u8_t *)&tcphdr->src, 2);
	break;
      case IP_PROTO_UDP:
	if(udphdr->chksum != 0) {
	  nat_chksum_adjust((u8_t *)&udphdr->chksum,(u8_t *)&oaddr, 4, (u8_t *)&iphdr->src.addr, 4);
	  udphdr->src = pcb->ass_port;
	  nat_chksum_adjust((u8_t *)&udphdr->chksum,(u8_t *)&src, 2, (u8_t *)&udphdr->src, 2);
	}
	break;
      case IP_PROTO_ICMP: //TODO: NOT AS ABOVE?
	icmphdr->id = pcb->ass_port;
	nat_chksum_adjust((u8_t *)&icmphdr->chksum,(u8_t *)&src, 2, (u8_t *)&icmphdr->id, 2);
	break;
      default:
	return ERR_RTE;
	break;
      }
    }
  }
  
  
#if NAT_TCP_END_DETECT
  if(pcb != NULL) {
    if(IPH_PROTO(iphdr) == IP_PROTO_TCP) {
      if(TCPH_FLAGS(tcphdr) & TCP_RST) {
	pcb->state = NAT_CLOSING;
      } else if(TCPH_FLAGS(tcphdr) & TCP_FIN) {
	if(pcb->state == NAT_OPEN) {
	  pcb->state = NAT_FIN;
	} else {
	  pcb->state = NAT_CLOSING;
	}
      }
    }
  }
#endif
   LWIP_DEBUGF(NAT_DEBUG, ("nat_input: NAT_EVENT_INPUT\n")); 
    NAT_EVENT_INPUT(&nat_ctrl, p, netif, ret);
 
  return ret;
}
/*-----------------------------------------------------------------------------------*/
/* 
 * nat_netif_add():
 *
 * Add a network interface to a list of lwIP netifs. The network interface will be 
 * part of a private network that access the external network through a single shared 
 * network interface.
 *  
 */
/*-----------------------------------------------------------------------------------*/
struct netif *
nat_netif_add(void* state, err_t (* init)(struct netif *netif))
{
  struct ip_addr ipaddr, netmask, gw;

  /* Get a private IP address that must be used by the host in the private network */
  if(ip_nxt > NAT_MAX_IP) {
    ip_nxt = NAT_MIN_IP;
  }

  /* Avoid setting a private IP address that matches the external interface IP address */
  if(nat_ctrl.nat_external_netif != NULL) {
    if(nat_ctrl.nat_external_netif->ip_addr.addr == ip_nxt) {
      /* Set next private address to be used */
      ip_nxt = ntohl(ip_nxt);
      ++ip_nxt;			     
      ip_nxt = htonl(ip_nxt);
    }
  }

  gw.addr = ip_nxt;
  ipaddr.addr = NAT_LOCAL_IP;
  netmask.addr = htonl(~(ntohl(gw.addr) ^ ntohl(ipaddr.addr))); /* Only packets with an IP address 
								   matching the one assigned for the 
								   private network must be forwarded 
								   on the network interface */
  
  /* Set next private address to be used */
  ip_nxt = ntohl(ip_nxt);
  ++ip_nxt;			     
  ip_nxt = htonl(ip_nxt);

  return netif_add(nat_ctrl.nat_external_netif, &ipaddr, &netmask, &gw, state, init, nat_input);
}
/*-----------------------------------------------------------------------------------*/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -