Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 17 Jun 2011 18:18:03 GMT
From:      "Alexander V. Chernikov" <melifaro@ipfw.ru>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   kern/157957: [libalias][patch] alias_ftp does not alias data sessions corretly
Message-ID:  <201106171818.p5HII3CC020555@red.freebsd.org>
Resent-Message-ID: <201106171820.p5HIKA6k083781@freefall.freebsd.org>

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

>Number:         157957
>Category:       kern
>Synopsis:       [libalias][patch] alias_ftp does not alias data sessions corretly
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Jun 17 18:20:10 UTC 2011
>Closed-Date:
>Last-Modified:
>Originator:     Alexander V. Chernikov
>Release:        9.0-CURRENT
>Organization:
JSC Meganet
>Environment:
FreeBSD zfscurr0.home.ipfw.ru 9.0-CURRENT FreeBSD 9.0-CURRENT #7 r222980M: Sat Jun 11 20:25:05 MSD 2011     root@zfscurr0.home.ipfw.ru:/var/xtmp/usj/obj/usr/src/sys/DEVEL  amd64

>Description:
[libalias][patch] alias_ftp does not alias data sessions corretly

CASE 1:
FTP server behind NAT with redirect to port 21.

Client is requesting data listing with ACTIVE mode:

Configuration:
ext interface: em0 10.0.0.83/24 alias 10.0.0.85/32
int interface: em1 10.1.5.83/24

FTP server address: 10.1.5.88
FTP client address: 10.0.0.92

FW & nat configuration:

10:53 [0] 82test# ipfw list
00001 allow ip from 10.0.0.1 to me
00002 allow ip from 10.0.0.5 to me
00100 nat 1 tcp from 10.0.0.92 to me recv em0 in
00200 nat 1 tcp from 10.1.5.88 to not me recv em1 xmit em0
65000 allow ip from any to any
65535 deny ip from any to any
11:19 [0] 82test# ipfw nat 1 show config
ipfw nat 1 config ip 10.0.0.83 log redirect_port tcp 10.1.5.88:21
10.0.0.85:21

CLIENT:

11:41 [0] m@zfscurr0 ftp 10.0.0.85
Connected to 10.0.0.85.
220 fbsd8.home.ipfw.ru FTP server (Version 6.00LS) ready.
Name (10.0.0.85:melifaro):
331 Password required for melifaro.
Password:
230 User melifaro logged in.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> passive
Passive mode: off; fallback to active mode: off.
ftp> dir
200 EPRT command successful.
150 Opening ASCII mode data connection for '/bin/ls'.
total 263642
....


TCPDUMP (server, em0):

11:21:50.022610 IP 10.0.0.92.56446 > 10.0.0.85.21: Flags [S], seq
261907120, win 65535, options [mss 1460,nop,wscale 3,sackOK,TS val
16347517 ecr 0], length 0
11:21:50.023260 IP 10.0.0.85.21 > 10.0.0.92.56446: Flags [S.], seq
1495315054, ack 261907121, win 65535, options [mss 1460,nop,wscale
3,sackOK,TS val 321201087 ecr 16347517], length 0
11:21:50.023485 IP 10.0.0.92.56446 > 10.0.0.85.21: Flags [.], ack 1, win
8326, options [nop,nop,TS val 16347517 ecr 321201087], length 0
..

11:21:59.747222 IP 10.0.0.92.56446 > 10.0.0.85.21: Flags [P.], ack 310,
win 8326, options [nop,nop,TS val 16348489 ecr 321202060], length 6
11:21:59.747849 IP 10.0.0.83.20 > 10.0.0.92.61729: Flags [S], seq
2889926664, win 65535, options [mss 1460,nop,wscale 3,sackOK,TS val
17722178 ecr 0], length 0

Note data session external ip is 10.0.0.83 (not 10.0.0.85 which should
be from client point of view)

If we're talking about multihomed NAT gateway with FTP redirects from
every uplink situation became even worse

CASE 2:
Same as case 1 except following:
* Client is using passive mode
* NAT configuration: ipfw nat 1 config ip 10.0.0.83 log redirect_port
tcp 10.1.5.88:21 10.0.0.85:21


CLIENT:

ftp> dir
229 Entering Extended Passive Mode (|||53055|)
200 EPRT command successful.

TCPDUMP (server, em0):
11:33:02.554517 IP 10.0.0.85.21 > 10.0.0.92.27178: Flags [P.], ack 56,
win 8326, options [nop,nop,TS val 2116529661 ecr 16414770], length 48
11:33:02.554952 IP 10.0.0.92.61156 > 10.0.0.85.53055: Flags [S], seq
1693610475, win 65535, options [mss 1460,nop,wscale 3,sackOK,TS val
16414770 ecr 0], length 0
11:33:02.554977 IP 10.0.0.85.53055 > 10.0.0.92.61156: Flags [R.], seq 0,
ack 1693610476, win 0, length 0

Server refuses connection for passive session setup


EXPLANATIONS:
Case 1:
State creation is done via hack with FindUdpTcpOut() which creates state
 assuming NAT alias address to be the same as redirect_port external
address.

Case 2:
State creation is not even done due to lack of inbound protocol handler
>How-To-Repeat:
Please see 'Full description' for lab setup
>Fix:
Apply an attached patch

Patch attached with submission follows:

Index: alias_db.c
===================================================================
--- alias_db.c	(revision 223170)
+++ alias_db.c	(working copy)
@@ -552,10 +552,6 @@ static void	IncrementalCleanup(struct libalias *);
 static void	DeleteLink(struct alias_link *);
 
 static struct alias_link *
-AddLink(struct libalias *, struct in_addr, struct in_addr, struct in_addr,
-    u_short, u_short, int, int);
-
-static struct alias_link *
 ReLink(struct alias_link *,
     struct in_addr, struct in_addr, struct in_addr,
     u_short, u_short, int, int);
@@ -937,7 +933,7 @@ DeleteLink(struct alias_link *lnk)
 }
 
 
-static struct alias_link *
+struct alias_link *
 AddLink(struct libalias *la, struct in_addr src_addr,
     struct in_addr dst_addr,
     struct in_addr alias_addr,
Index: libalias.3
===================================================================
--- libalias.3	(revision 223170)
+++ libalias.3	(working copy)
@@ -1013,6 +1013,29 @@ If this results in a conflict, then port numbers a
 a unique aliasing link can be established.
 In an alternate operating mode, the first choice of an aliasing port is also
 random and unrelated to the local port number.
+.Ss WORKING WITH DYNAMIC LINKS
+Several function permit creating such dynamic links directly
+(this can be useful in modules like alias_ftp where every data transfer uses
+separate TCP connection)
+.Pp
+.Ft struct alias_link *
+.Fn AddLink "struct libalias *" "struct in_addr src_addr" "struct in_addr dst_addr" \
+"struct in_addr alias_addr" "u_short src_port" "u_short dst_port" \
+"int alias_param" "int link_type"
+.Bd -ragged -offset indent
+This function adds new state to instance hash table. Zero can be specified instead of
+dst_address and/or dst port. This makes link partially specified dynamic. However
+due to hashing method such links can be resolved on inbound (ext -> int) only.
+Function returns pointer to 
+.Vt "struct alias_link"
+which is not exported. One can extract needed fields via separate functions.
+Please see section 
+.Pa External data access/modification
+from
+.Pa alias_local.h
+for complete list
+.Ed
+.Pp
 .Sh MODULAR ARCHITECTURE (AND Xr ipfw 4 Sh SUPPORT)
 One of the latest improvements to
 .Nm
Index: alias_ftp.c
===================================================================
--- alias_ftp.c	(revision 223170)
+++ alias_ftp.c	(working copy)
@@ -102,9 +102,11 @@ __FBSDID("$FreeBSD$");
 static void
 AliasHandleFtpOut(struct libalias *, struct ip *, struct alias_link *,	
 		  int maxpacketsize);
+static void
+AliasHandleFtpIn(struct libalias *, struct ip *, struct alias_link *);
 
 static int 
-fingerprint(struct libalias *la, struct alias_data *ah)
+fingerprint_out(struct libalias *la, struct alias_data *ah)
 {
 
 	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL || 
@@ -117,21 +119,49 @@ static int
 }
 
 static int 
-protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
+fingerprint_in(struct libalias *la, struct alias_data *ah)
 {
+
+	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL)
+		return (-1);
+	if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER
+	    || ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
+		return (0);
+	return (-1);
+}
+
+static int 
+protohandler_out(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
 	
 	AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize);
 	return (0);
 }
 
+
+static int 
+protohandler_in(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+	
+	AliasHandleFtpIn(la, pip, ah->lnk);
+	return (0);
+}
+
 struct proto_handler handlers[] = {
 	{ 
 	  .pri = 80, 
 	  .dir = OUT, 
 	  .proto = TCP, 
-	  .fingerprint = &fingerprint, 
-	  .protohandler = &protohandler
+	  .fingerprint = &fingerprint_out, 
+	  .protohandler = &protohandler_out
 	}, 
+	{ 
+	  .pri = 80, 
+	  .dir = IN, 
+	  .proto = TCP, 
+	  .fingerprint = &fingerprint_in,
+	  .protohandler = &protohandler_in
+	}, 
 	{ EOH }
 };
 
@@ -256,6 +286,60 @@ AliasHandleFtpOut(
 	}
 }
 
+static void
+AliasHandleFtpIn(
+    struct libalias *la,
+    struct ip *pip,		/* IP packet to examine/patch */
+    struct alias_link *lnk)	/* The link to go through (aliased port) */
+{
+       int hlen, tlen, dlen, pflags;
+       char *sptr;
+       struct tcphdr *tc;
+
+/* Calculate data length of TCP packet */
+       tc = (struct tcphdr *)ip_next(pip);
+       hlen = (pip->ip_hl + tc->th_off) << 2;
+       tlen = ntohs(pip->ip_len);
+       dlen = tlen - hlen;
+
+/* Place string pointer and beginning of data */
+       sptr = (char *)pip;
+       sptr += hlen;
+
+/*
+ * Check that data length is not too long and previous message was
+ * properly terminated with CRLF.
+ */
+       pflags = GetProtocolFlags(lnk);
+       if ((dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) &&
+		       (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) &&
+		       ((ParseFtpPortCommand(la, sptr, dlen)) ||
+			(ParseFtpEprtCommand(la, sptr, dlen)))) {
+/*
+ * Alias active mode client requesting data from server behind NAT
+ * We need to alias server->client connection to external address
+ * client is connecting to
+ */
+		AddLink(la, GetOriginalAddress(lnk), la->true_addr,
+				GetAliasAddress(lnk),
+				htons(FTP_CONTROL_PORT_NUMBER - 1),
+				htons(la->true_port),
+				GET_ALIAS_PORT, IPPROTO_TCP);
+       }
+/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
+
+       if (dlen) {	     /* only if there's data */
+	       sptr = (char *)pip;     /* start over at beginning */
+	       tlen = ntohs(pip->ip_len);      /* recalc tlen, pkt may
+						* have grown */
+	       if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n')
+		       pflags &= ~WAIT_CRLF;
+	       else
+		       pflags |= WAIT_CRLF;
+	       SetProtocolFlags(lnk, pflags);
+       }
+}
+
 static int
 ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen)
 {
@@ -577,8 +661,9 @@ NewFtpMessage(struct libalias *la, struct ip *pip,
 		return;
 
 /* Establish link to address and port found in FTP control message. */
-	ftp_lnk = FindUdpTcpOut(la, la->true_addr, GetDestAddress(lnk),
-	    htons(la->true_port), 0, IPPROTO_TCP, 1);
+       ftp_lnk = AddLink(la, la->true_addr, GetDestAddress(lnk),
+		       GetAliasAddress(lnk), htons(la->true_port),
+		       0, GET_ALIAS_PORT, IPPROTO_TCP);
 
 	if (ftp_lnk != NULL) {
 		int slen, hlen, tlen, dlen;
Index: alias_local.h
===================================================================
--- alias_local.h	(revision 223170)
+++ alias_local.h	(working copy)
@@ -67,6 +67,9 @@
 #define LINK_TABLE_OUT_SIZE        4001
 #define LINK_TABLE_IN_SIZE         4001
 
+#define GET_ALIAS_PORT		  -1
+#define GET_ALIAS_ID	GET_ALIAS_PORT
+
 struct proxy_entry;
 
 struct libalias {
@@ -249,6 +252,10 @@ DifferentialChecksum(u_short * _cksum, void * _new
 
 /* Internal data access */
 struct alias_link *
+AddLink(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr,
+    struct in_addr alias_addr, u_short src_port, u_short dst_port,
+    int alias_param, int link_type);
+struct alias_link *
 FindIcmpIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr,
     u_short _id_alias, int _create);
 struct alias_link *


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



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