Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 26 Jan 1999 03:07:16 -0800 (PST)
From:      wes@bogon.net
To:        FreeBSD-gnats-submit@FreeBSD.ORG
Subject:   bin/9696: Updates to natd.c
Message-ID:  <199901261107.DAA01135@bogon.net>

next in thread | raw e-mail | index | archive | help

>Number:         9696
>Category:       bin
>Synopsis:       Added ability to specify port ranges in natd command line
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Tue Jan 26 03:10:00 PST 1999
>Closed-Date:
>Last-Modified:
>Originator:     Wes Santee
>Release:        FreeBSD 3.0-RELEASE i386
>Organization:
>Environment:

	N/A.  Diff enclosed from natd.c v1.8

>Description:

	I've added the ability to specify port-ranges with the
	redirect_port and permanent_link command line options in
	natd.  I've not extensively tested the code, but it appears to
	be working for me.

	The new linkspec for redirect_port is of the format:
	proto targetIP:targetPORT[-targetPORT]
	  [aliasIP:]aliasPORT[-aliasPORT]
	  [remoteIP[:remotePORT[-remotePORT]]]

	For example:

	redirect_port tcp 10.0.0.1:2300-2399 3300-3399

	will redirect incomming connections on ports 3300-3399 to host
	10.0.0.1, ports 2300-2399.  The mapping is 1:1 meaning port
	3300 maps to 2300, 3301 maps to 2301, etc.  The targetPORT
	range and aliasPORT range need not be the same numerically,
	but must have the same size.  If remotePORT is not specified,
	it is assumed to be all ports.  If remotePORT is specified, it
	must match the size of targetPORT, or be 0 (all ports).

	The new linkspec for permanent_link is of the format:
	proto targetIP:targetPORT[-targetPORT]
	  sourceIP:sourcePORT[-sourcePORT]
	  aliasPORT[-aliasPORT]

	For example:

	permanent_link tcp 10.0.0.1:2300-2399 outside:0 3300-3399

	will setup 100 permanent links such that packets destined for
	ports 3300-3399 on this machine from the outside machine (any
	port) will be sent to ports 2300-2399 on machine 10.0.0.1.  As
	with redirect_port, there is a 1:1 mapping.  In addition, the
	source port range must either be 0 (all ports), or be the same
	size as the targetPORT range.

	[N.B.: This linkspec is based off the manpage.  It seemed
	somehow different to me in the code itself, but I left the
	order of parsing in the code the way it was.]

	NOTES:
	- I haven't extensively tested this.  It appears to be working
	on my system, but I'd definately consider this test code.
	- I'm not even sure these additions are considered "a good
	thing" so wanting to leave it out entirely may be the best
	option. 
	- This is my first code submission so take it easy on me,
	okay? :)

	Here is a sample natd.conf that I'm using (invoked via the
	command "natd -config /etc/natd.conf -n xl0" on my system:

	use_sockets yes
	unregistered_only yes
	same_ports yes
	deny_incoming no
	
	## Allow Direct Play 5 apps to be hostable on the Win95 machine
	redirect_port tcp kryten:47624 47624
	redirect_port udp kryten:47624 47624
	redirect_port tcp kryten:2300-2399 2300-2399
	redirect_port udp kryten:2300-2399 2300-2399
	
	## Everything else that comes in goes to 10.0.0.1
	redirect_address 10.0.0.1 0.0.0.0

>How-To-Repeat:

	Apply the patch against v1.8 (by way of the RCS Id) of natd.c
	and compile.

>Fix:
	
*** natd.c.orig	Tue Jan 26 02:34:56 1999
--- natd.c	Tue Jan 26 02:34:32 1999
***************
*** 9,15 ****
   *
   * Ari Suutari <suutari@iki.fi>
   *
!  * $Id: natd.c,v 1.8 1997/12/27 19:31:11 alex Exp $
   */
  
  #include <sys/types.h>
--- 9,15 ----
   *
   * Ari Suutari <suutari@iki.fi>
   *
!  * $Id: natd.c,v 1.9 1999/01/26 09:17:10 wes Exp wes $
   */
  
  #include <sys/types.h>
***************
*** 48,53 ****
--- 48,72 ----
  #define	DEFAULT_SERVICE	"natd"
  
  /*
+  * Definition of a port range, and macros to deal with values.
+  * FORMAT:  HI 16-bits == first port in range, 0 == all ports.
+  *          LO 16-bits == number of ports in range
+  * NOTES:   - Port values are not stored in network byte order.
+  */
+ 
+ typedef u_long port_range;
+ 
+ #define GETLOPORT(x)     ((x) >> 0x10)
+ #define GETNUMPORTS(x)   ((x) & 0x0000ffff)
+ #define GETHIPORT(x)     (GETLOPORT((x)) + GETNUMPORTS((x)))
+ 
+ /* Set y to be the low-port value in port_range variable x. */
+ #define SETLOPORT(x,y)   ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
+ 
+ /* Set y to be the number of ports in port_range variable x. */
+ #define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
+ 
+ /*
   * Function prototypes.
   */
  
***************
*** 67,74 ****
  static void	SetupAddressRedirect (char* parms);
  static void	StrToAddr (char* str, struct in_addr* addr);
  static u_short  StrToPort (char* str, char* proto);
  static int 	StrToProto (char* str);
! static u_short  StrToAddrAndPort (char* str, struct in_addr* addr, char* proto);
  static void	ParseArgs (int argc, char** argv);
  static void	FlushPacketBuffer (int fd);
  
--- 86,94 ----
  static void	SetupAddressRedirect (char* parms);
  static void	StrToAddr (char* str, struct in_addr* addr);
  static u_short  StrToPort (char* str, char* proto);
+ static int      StrToPortRange (char* str, char* proto, port_range *portRange);
  static int 	StrToProto (char* str);
! static int      StrToAddrAndPortRange (char* str, struct in_addr* addr, char* proto, port_range *portRange);
  static void	ParseArgs (int argc, char** argv);
  static void	FlushPacketBuffer (int fd);
  
***************
*** 894,900 ****
  	{ PermanentLink,
  		0,
  		String,
! 	        "tcp|udp src:port dst:port alias",
  		"define permanent link for incoming connection",
  		"permanent_link",
  		NULL },
--- 914,920 ----
  	{ PermanentLink,
  		0,
  		String,
! 	        "tcp|udp src:port_range dst:port_range alias_range",
  		"define permanent link for incoming connection",
  		"permanent_link",
  		NULL },
***************
*** 902,910 ****
  	{ RedirectPort,
  		0,
  		String,
! 	        "tcp|udp local_addr:local_port [public_addr:]public_port"
! 	 	" [remote_addr[:remote_port]]",
! 		"redirect a port for incoming traffic",
  		"redirect_port",
  		NULL },
  
--- 922,930 ----
  	{ RedirectPort,
  		0,
  		String,
! 	        "tcp|udp local_addr:local_port_range [public_addr:]public_port_range"
! 	 	" [remote_addr[:remote_port_range]]",
! 		"redirect a port (or ports) for incoming traffic",
  		"redirect_port",
  		NULL },
  
***************
*** 1160,1170 ****
  	char*		ptr;
  	struct in_addr	srcAddr;
  	struct in_addr	dstAddr;
! 	u_short		srcPort;
! 	u_short		dstPort;
! 	u_short		aliasPort;
  	int		proto;
  	char*		protoName;
  
  	strcpy (buf, parms);
  /*
--- 1180,1195 ----
  	char*		ptr;
  	struct in_addr	srcAddr;
  	struct in_addr	dstAddr;
! 	port_range      portRange;
! 	u_short         srcPort       = 0;
! 	u_short         dstPort       = 0;
! 	u_short         aliasPort     = 0;
! 	u_short         numSrcPorts   = 0;
! 	u_short         numDstPorts   = 0;
! 	u_short         numAliasPorts = 0;
  	int		proto;
  	char*		protoName;
+ 	int             i;
  
  	strcpy (buf, parms);
  /*
***************
*** 1182,1188 ****
  	if (!ptr)
  		errx (1, "permanent_link: missing src address");
  
! 	srcPort = StrToAddrAndPort (ptr, &srcAddr, protoName);
  /*
   * Extract destination address.
   */
--- 1207,1218 ----
  	if (!ptr)
  		errx (1, "permanent_link: missing src address");
  
! 	if (StrToAddrAndPortRange (ptr, &srcAddr, protoName, &portRange) != 0)
! 	        errx (1, "permanent_link: invalid source port range");
! 
! 	srcPort     = GETLOPORT(portRange);
! 	numSrcPorts = GETNUMPORTS(portRange);
! 
  /*
   * Extract destination address.
   */
***************
*** 1190,1211 ****
  	if (!ptr)
  		errx (1, "permanent_link: missing dst address");
  
! 	dstPort = StrToAddrAndPort (ptr, &dstAddr, protoName);
  /*
!  * Export alias port.
   */
  	ptr = strtok (NULL, " \t");
  	if (!ptr)
  		errx (1, "permanent_link: missing alias port");
  
! 	aliasPort = StrToPort (ptr, protoName);
  
! 	PacketAliasPermanentLink (srcAddr,
! 				  srcPort,
! 				  dstAddr,
! 				  dstPort,
! 				  aliasPort,
! 				  proto);
  }
  
  void SetupPortRedirect (char* parms)
--- 1220,1268 ----
  	if (!ptr)
  		errx (1, "permanent_link: missing dst address");
  
! 	if (StrToAddrAndPortRange (ptr, &srcAddr, protoName, &portRange) != 0)
! 	        errx (1, "permanent_link: invalid destination port range");
! 
! 	dstPort     = GETLOPORT(portRange);
! 	numDstPorts = GETNUMPORTS(portRange);
! 
  /*
!  * Export alias port range.
   */
  	ptr = strtok (NULL, " \t");
  	if (!ptr)
  		errx (1, "permanent_link: missing alias port");
  
! 	if (StrToPortRange (ptr, protoName, &portRange) != 0)
! 	        errx (1, "permanent_link: invalid alias port range");
! 
! 	aliasPort     = GETLOPORT(portRange);
! 	numAliasPorts = GETNUMPORTS(portRange);
  
! /*
!  * Make sure port ranges match up, then add the permanent links.
!  */
! 
! 	if (numSrcPorts != numAliasPorts)
! 	        errx (1, "permanent_link: port ranges must be equal in size");
! 
! 	/* Destination port range is allowed to be '0' which means all ports. */
! 	if (numSrcPorts != numDstPorts && numDstPorts != 1 && dstPort != 0)
! 	        errx (1, "permanent_link: dst port must 0 or equal to src port range in size");
! 
! 	for (i = 0 ; i < numAliasPorts ; ++i) {
! 	        /* If dstPort is all ports, set it to 0 */
! 	        u_short dstPortCopy = dstPort + i;
! 	        if (numDstPorts == 1 && dstPort == 0)
! 		        dstPortCopy = 0;
! 
! 	        PacketAliasPermanentLink (srcAddr,
! 					  htons(srcPort + i),
! 					  dstAddr,
! 					  htons(dstPortCopy),
! 					  htons(aliasPort + i),
! 					  proto);
! 	}
  }
  
  void SetupPortRedirect (char* parms)
***************
*** 1215,1226 ****
  	struct in_addr	localAddr;
  	struct in_addr	publicAddr;
  	struct in_addr	remoteAddr;
! 	u_short		localPort;
! 	u_short		publicPort;
! 	u_short		remotePort;
  	int		proto;
  	char*		protoName;
  	char*		separator;
  
  	strcpy (buf, parms);
  /*
--- 1272,1288 ----
  	struct in_addr	localAddr;
  	struct in_addr	publicAddr;
  	struct in_addr	remoteAddr;
! 	port_range      portRange;
! 	u_short         localPort      = 0;
! 	u_short         publicPort     = 0;
! 	u_short         remotePort     = 0;
! 	u_short         numLocalPorts  = 0;
! 	u_short         numPublicPorts = 0;
! 	u_short         numRemotePorts = 0;
  	int		proto;
  	char*		protoName;
  	char*		separator;
+ 	int             i;
  
  	strcpy (buf, parms);
  /*
***************
*** 1238,1244 ****
  	if (!ptr)
  		errx (1, "redirect_port: missing local address");
  
! 	localPort = StrToAddrAndPort (ptr, &localAddr, protoName);
  /*
   * Extract public port and optinally address.
   */
--- 1300,1311 ----
  	if (!ptr)
  		errx (1, "redirect_port: missing local address");
  
! 	if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 )
! 	        errx (1, "redirect_port: invalid local port range");
! 
! 	localPort     = GETLOPORT(portRange);
! 	numLocalPorts = GETNUMPORTS(portRange);
! 
  /*
   * Extract public port and optinally address.
   */
***************
*** 1247,1291 ****
  		errx (1, "redirect_port: missing public port");
  
  	separator = strchr (ptr, ':');
! 	if (separator)
! 		publicPort = StrToAddrAndPort (ptr, &publicAddr, protoName);
  	else {
- 
  		publicAddr.s_addr = INADDR_ANY;
! 		publicPort = StrToPort (ptr, protoName);
  	}
  
  /*
   * Extract remote address and optionally port.
   */
  	ptr = strtok (NULL, " \t");
  	if (ptr) {
- 
- 
  		separator = strchr (ptr, ':');
  		if (separator)
! 			remotePort = StrToAddrAndPort (ptr,
! 						       &remoteAddr,
! 						       protoName);
  		else {
! 
! 			remotePort = 0;
  			StrToAddr (ptr, &remoteAddr);
  		}
  	}
  	else {
! 
! 		remotePort = 0;
  		remoteAddr.s_addr = INADDR_ANY;
  	}
  
! 	PacketAliasRedirectPort (localAddr,
! 				 localPort,
! 				 remoteAddr,
! 				 remotePort,
! 				 publicAddr,
! 				 publicPort,
! 				 proto);
  }
  
  void SetupAddressRedirect (char* parms)
--- 1314,1380 ----
  		errx (1, "redirect_port: missing public port");
  
  	separator = strchr (ptr, ':');
! 	if (separator) {
! 	        if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 )
! 		        errx (1, "redirect_port: invalid public port range");
! 	}
  	else {
  		publicAddr.s_addr = INADDR_ANY;
! 		if (StrToPortRange (ptr, protoName, &portRange) != 0)
! 		        errx (1, "redirect_port: invalid public port range");
  	}
  
+ 	publicPort     = GETLOPORT(portRange);
+ 	numPublicPorts = GETNUMPORTS(portRange);
+ 
  /*
   * Extract remote address and optionally port.
   */
  	ptr = strtok (NULL, " \t");
  	if (ptr) {
  		separator = strchr (ptr, ':');
  		if (separator)
! 		        if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0)
! 			        errx (1, "redirect_port: invalid remote port range");
  		else {
! 		        SETLOPORT(portRange, 0);
! 			SETNUMPORTS(portRange, 1);
  			StrToAddr (ptr, &remoteAddr);
  		}
  	}
  	else {
! 	        SETLOPORT(portRange, 0);
! 		SETNUMPORTS(portRange, 1);
  		remoteAddr.s_addr = INADDR_ANY;
  	}
  
! 	remotePort     = GETLOPORT(portRange);
! 	numRemotePorts = GETNUMPORTS(portRange);
! 
! /*
!  * Make sure port ranges match up, then add the redirect ports.
!  */
! 	if (numLocalPorts != numPublicPorts)
! 	        errx (1, "redirect_port: port ranges must be equal in size");
! 
! 	/* Remote port range is allowed to be '0' which means all ports. */
! 	if (numRemotePorts != numLocalPorts && numRemotePorts != 1 && remotePort != 0)
! 	        errx (1, "redirect_port: remote port must be 0 or equal to local port range in size");
! 
! 	for (i = 0 ; i < numPublicPorts ; ++i) {
! 	        /* If remotePort is all ports, set it to 0. */
! 	        u_short remotePortCopy = remotePort + i;
! 	        if (numRemotePorts == 1 && remotePort == 0)
! 		        remotePortCopy = 0;
! 
! 	        PacketAliasRedirectPort (localAddr,
! 					 htons(localPort + i),
! 					 remoteAddr,
! 					 htons(remotePortCopy),
! 					 publicAddr,
! 					 htons(publicPort + i),
! 					 proto);
! 	}
  }
  
  void SetupAddressRedirect (char* parms)
***************
*** 1346,1351 ****
--- 1435,1484 ----
  	return sp->s_port;
  }
  
+ int StrToPortRange (char* str, char* proto, port_range *portRange)
+ {
+ 	char*           sep;
+ 	struct servent*	sp;
+ 	char*		end;
+ 	u_short         loPort;
+ 	u_short         hiPort;
+ 	
+ 	/* First see if this is a service, return corresponding port if so. */
+ 	sp = getservbyname (str,proto);
+ 	if (sp) {
+ 	        SETLOPORT(*portRange, ntohs(sp->s_port));
+ 		SETNUMPORTS(*portRange, 1);
+ 		return 0;
+ 	}
+ 	        
+ 	/* Not a service, see if it's a single port or port range. */
+ 	sep = strchr (str, '-');
+ 	if (sep == NULL) {
+ 	        SETLOPORT(*portRange, strtol(str, &end, 10));
+ 		if (end != str) {
+ 		        /* Single port. */
+ 		        SETNUMPORTS(*portRange, 1);
+ 			return 0;
+ 		}
+ 
+ 		/* Error in port range field. */
+ 		errx (1, "unknown service %s/%s", str, proto);
+ 	}
+ 
+ 	/* Port range, get the values and sanity check. */
+ 	sscanf (str, "%hu-%hu", &loPort, &hiPort);
+ 	SETLOPORT(*portRange, loPort);
+ 	SETNUMPORTS(*portRange, 0);	/* Error by default */
+ 	if (loPort <= hiPort)
+ 	        SETNUMPORTS(*portRange, hiPort - loPort + 1);
+ 
+ 	if (GETNUMPORTS(*portRange) == 0)
+ 	        errx (1, "invalid port range %s", str);
+ 
+ 	return 0;
+ }
+ 
+ 
  int StrToProto (char* str)
  {
  	if (!strcmp (str, "tcp"))
***************
*** 1357,1363 ****
  	errx (1, "unknown protocol %s. Expected tcp or udp", str);
  }
  
! u_short StrToAddrAndPort (char* str, struct in_addr* addr, char* proto)
  {
  	char*	ptr;
  
--- 1490,1496 ----
  	errx (1, "unknown protocol %s. Expected tcp or udp", str);
  }
  
! int StrToAddrAndPortRange (char* str, struct in_addr* addr, char* proto, port_range *portRange)
  {
  	char*	ptr;
  
***************
*** 1369,1374 ****
  	++ptr;
  
  	StrToAddr (str, addr);
! 	return StrToPort (ptr, proto);
  }
- 
--- 1502,1506 ----
  	++ptr;
  
  	StrToAddr (str, addr);
! 	return StrToPortRange (ptr, proto, portRange);
  }


>Release-Note:
>Audit-Trail:
>Unformatted:

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199901261107.DAA01135>