xref: /freebsd/sys/netinet/libalias/alias_ftp.c (revision 9e5787d2284e187abb5b654d924394a65772e004)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2001 Charles Mott <cm@linktel.net>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 /*
33     Alias_ftp.c performs special processing for FTP sessions under
34     TCP.  Specifically, when a PORT/EPRT command from the client
35     side or 227/229 reply from the server is sent, it is intercepted
36     and modified.  The address is changed to the gateway machine
37     and an aliasing port is used.
38 
39     For this routine to work, the message must fit entirely into a
40     single TCP packet.  This is typically the case, but exceptions
41     can easily be envisioned under the actual specifications.
42 
43     Probably the most troubling aspect of the approach taken here is
44     that the new message will typically be a different length, and
45     this causes a certain amount of bookkeeping to keep track of the
46     changes of sequence and acknowledgment numbers, since the client
47     machine is totally unaware of the modification to the TCP stream.
48 
49 
50     References: RFC 959, RFC 2428.
51 
52     Initial version:  August, 1996  (cjm)
53 
54     Version 1.6
55 	 Brian Somers and Martin Renters identified an IP checksum
56 	 error for modified IP packets.
57 
58     Version 1.7:  January 9, 1996 (cjm)
59 	 Differential checksum computation for change
60 	 in IP packet length.
61 
62     Version 2.1:  May, 1997 (cjm)
63 	 Very minor changes to conform with
64 	 local/global/function naming conventions
65 	 within the packet aliasing module.
66 
67     Version 3.1:  May, 2000 (eds)
68 	 Add support for passive mode, alias the 227 replies.
69 
70     See HISTORY file for record of revisions.
71 */
72 
73 /* Includes */
74 #ifdef _KERNEL
75 #include <sys/param.h>
76 #include <sys/ctype.h>
77 #include <sys/systm.h>
78 #include <sys/kernel.h>
79 #include <sys/module.h>
80 #else
81 #include <ctype.h>
82 #include <errno.h>
83 #include <sys/types.h>
84 #include <stdio.h>
85 #include <string.h>
86 #endif
87 
88 #include <netinet/in_systm.h>
89 #include <netinet/in.h>
90 #include <netinet/ip.h>
91 #include <netinet/tcp.h>
92 
93 #ifdef _KERNEL
94 #include <netinet/libalias/alias.h>
95 #include <netinet/libalias/alias_local.h>
96 #include <netinet/libalias/alias_mod.h>
97 #else
98 #include "alias_local.h"
99 #include "alias_mod.h"
100 #endif
101 
102 #define FTP_CONTROL_PORT_NUMBER 21
103 
104 static void
105 AliasHandleFtpOut(struct libalias *, struct ip *, struct alias_link *,
106     int maxpacketsize);
107 static void
108 AliasHandleFtpIn(struct libalias *, struct ip *, struct alias_link *);
109 
110 static int
111 fingerprint_out(struct libalias *la, struct alias_data *ah)
112 {
113 
114 	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
115 	    ah->maxpktsize == 0)
116 		return (-1);
117 	if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER ||
118 	    ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
119 		return (0);
120 	return (-1);
121 }
122 
123 static int
124 fingerprint_in(struct libalias *la, struct alias_data *ah)
125 {
126 
127 	if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL)
128 		return (-1);
129 	if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER ||
130 	    ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
131 		return (0);
132 	return (-1);
133 }
134 
135 static int
136 protohandler_out(struct libalias *la, struct ip *pip, struct alias_data *ah)
137 {
138 
139 	AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize);
140 	return (0);
141 }
142 
143 
144 static int
145 protohandler_in(struct libalias *la, struct ip *pip, struct alias_data *ah)
146 {
147 
148 	AliasHandleFtpIn(la, pip, ah->lnk);
149 	return (0);
150 }
151 
152 struct proto_handler handlers[] = {
153 	{
154 	  .pri = 80,
155 	  .dir = OUT,
156 	  .proto = TCP,
157 	  .fingerprint = &fingerprint_out,
158 	  .protohandler = &protohandler_out
159 	},
160 	{
161 	  .pri = 80,
162 	  .dir = IN,
163 	  .proto = TCP,
164 	  .fingerprint = &fingerprint_in,
165 	  .protohandler = &protohandler_in
166 	},
167 	{ EOH }
168 };
169 
170 static int
171 mod_handler(module_t mod, int type, void *data)
172 {
173 	int error;
174 
175 	switch (type) {
176 	case MOD_LOAD:
177 		error = 0;
178 		LibAliasAttachHandlers(handlers);
179 		break;
180 	case MOD_UNLOAD:
181 		error = 0;
182 		LibAliasDetachHandlers(handlers);
183 		break;
184 	default:
185 		error = EINVAL;
186 	}
187 	return (error);
188 }
189 
190 #ifdef _KERNEL
191 static
192 #endif
193 moduledata_t alias_mod = {
194        "alias_ftp", mod_handler, NULL
195 };
196 
197 #ifdef	_KERNEL
198 DECLARE_MODULE(alias_ftp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
199 MODULE_VERSION(alias_ftp, 1);
200 MODULE_DEPEND(alias_ftp, libalias, 1, 1, 1);
201 #endif
202 
203 #define FTP_CONTROL_PORT_NUMBER 21
204 #define MAX_MESSAGE_SIZE	128
205 
206 /* FTP protocol flags. */
207 #define WAIT_CRLF		0x01
208 
209 enum ftp_message_type {
210 	FTP_PORT_COMMAND,
211 	FTP_EPRT_COMMAND,
212 	FTP_227_REPLY,
213 	FTP_229_REPLY,
214 	FTP_UNKNOWN_MESSAGE
215 };
216 
217 static int	ParseFtpPortCommand(struct libalias *la, char *, int);
218 static int	ParseFtpEprtCommand(struct libalias *la, char *, int);
219 static int	ParseFtp227Reply(struct libalias *la, char *, int);
220 static int	ParseFtp229Reply(struct libalias *la, char *, int);
221 static void	NewFtpMessage(struct libalias *la, struct ip *, struct alias_link *, int, int);
222 
223 static void
224 AliasHandleFtpOut(
225     struct libalias *la,
226     struct ip *pip,		/* IP packet to examine/patch */
227     struct alias_link *lnk,	/* The link to go through (aliased port) */
228     int maxpacketsize		/* The maximum size this packet can grow to
229 	(including headers) */ )
230 {
231 	int hlen, tlen, dlen, pflags;
232 	char *sptr;
233 	struct tcphdr *tc;
234 	int ftp_message_type;
235 
236 /* Calculate data length of TCP packet */
237 	tc = (struct tcphdr *)ip_next(pip);
238 	hlen = (pip->ip_hl + tc->th_off) << 2;
239 	tlen = ntohs(pip->ip_len);
240 	dlen = tlen - hlen;
241 
242 /* Place string pointer and beginning of data */
243 	sptr = (char *)pip;
244 	sptr += hlen;
245 
246 /*
247  * Check that data length is not too long and previous message was
248  * properly terminated with CRLF.
249  */
250 	pflags = GetProtocolFlags(lnk);
251 	if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) {
252 		ftp_message_type = FTP_UNKNOWN_MESSAGE;
253 
254 		if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) {
255 /*
256  * When aliasing a client, check for the PORT/EPRT command.
257  */
258 			if (ParseFtpPortCommand(la, sptr, dlen))
259 				ftp_message_type = FTP_PORT_COMMAND;
260 			else if (ParseFtpEprtCommand(la, sptr, dlen))
261 				ftp_message_type = FTP_EPRT_COMMAND;
262 		} else {
263 /*
264  * When aliasing a server, check for the 227/229 reply.
265  */
266 			if (ParseFtp227Reply(la, sptr, dlen))
267 				ftp_message_type = FTP_227_REPLY;
268 			else if (ParseFtp229Reply(la, sptr, dlen)) {
269 				ftp_message_type = FTP_229_REPLY;
270 				la->true_addr.s_addr = pip->ip_src.s_addr;
271 			}
272 		}
273 
274 		if (ftp_message_type != FTP_UNKNOWN_MESSAGE)
275 			NewFtpMessage(la, pip, lnk, maxpacketsize, ftp_message_type);
276 	}
277 /* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
278 
279 	if (dlen) {		/* only if there's data */
280 		sptr = (char *)pip;	/* start over at beginning */
281 		tlen = ntohs(pip->ip_len);	/* recalc tlen, pkt may
282 						 * have grown */
283 		if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n')
284 			pflags &= ~WAIT_CRLF;
285 		else
286 			pflags |= WAIT_CRLF;
287 		SetProtocolFlags(lnk, pflags);
288 	}
289 }
290 
291 static void
292 AliasHandleFtpIn(struct libalias *la,
293     struct ip *pip,		/* IP packet to examine/patch */
294     struct alias_link *lnk)	/* The link to go through (aliased port) */
295 {
296 	int hlen, tlen, dlen, pflags;
297 	char *sptr;
298 	struct tcphdr *tc;
299 
300 	/* Calculate data length of TCP packet */
301 	tc = (struct tcphdr *)ip_next(pip);
302 	hlen = (pip->ip_hl + tc->th_off) << 2;
303 	tlen = ntohs(pip->ip_len);
304 	dlen = tlen - hlen;
305 
306 	/* Place string pointer and beginning of data */
307 	sptr = (char *)pip;
308 	sptr += hlen;
309 
310 	/*
311 	 * Check that data length is not too long and previous message was
312 	 * properly terminated with CRLF.
313 	 */
314 	pflags = GetProtocolFlags(lnk);
315 	if (dlen <= MAX_MESSAGE_SIZE && (pflags & WAIT_CRLF) == 0 &&
316 	    ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER &&
317 	    (ParseFtpPortCommand(la, sptr, dlen) != 0 ||
318 	     ParseFtpEprtCommand(la, sptr, dlen) != 0)) {
319 		/*
320 		 * Alias active mode client requesting data from server
321 		 * behind NAT.  We need to alias server->client connection
322 		 * to external address client is connecting to.
323 		 */
324 		AddLink(la, GetOriginalAddress(lnk), la->true_addr,
325 		    GetAliasAddress(lnk), htons(FTP_CONTROL_PORT_NUMBER - 1),
326 		    htons(la->true_port), GET_ALIAS_PORT, IPPROTO_TCP);
327 	}
328 	/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
329 	if (dlen) {
330 		sptr = (char *)pip;		/* start over at beginning */
331 		tlen = ntohs(pip->ip_len);	/* recalc tlen, pkt may
332 						 * have grown.
333 						 */
334 		if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n')
335 			pflags &= ~WAIT_CRLF;
336 		else
337 			pflags |= WAIT_CRLF;
338 		SetProtocolFlags(lnk, pflags);
339        }
340 }
341 
342 static int
343 ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen)
344 {
345 	char ch;
346 	int i, state;
347 	u_int32_t addr;
348 	u_short port;
349 	u_int8_t octet;
350 
351 	/* Format: "PORT A,D,D,R,PO,RT". */
352 
353 	/* Return if data length is too short. */
354 	if (dlen < 18)
355 		return (0);
356 
357 	if (strncasecmp("PORT ", sptr, 5))
358 		return (0);
359 
360 	addr = port = octet = 0;
361 	state = 0;
362 	for (i = 5; i < dlen; i++) {
363 		ch = sptr[i];
364 		switch (state) {
365 		case 0:
366 			if (isspace(ch))
367 				break;
368 			else
369 				state++;
370 		case 1:
371 		case 3:
372 		case 5:
373 		case 7:
374 		case 9:
375 		case 11:
376 			if (isdigit(ch)) {
377 				octet = ch - '0';
378 				state++;
379 			} else
380 				return (0);
381 			break;
382 		case 2:
383 		case 4:
384 		case 6:
385 		case 8:
386 			if (isdigit(ch))
387 				octet = 10 * octet + ch - '0';
388 			else if (ch == ',') {
389 				addr = (addr << 8) + octet;
390 				state++;
391 			} else
392 				return (0);
393 			break;
394 		case 10:
395 		case 12:
396 			if (isdigit(ch))
397 				octet = 10 * octet + ch - '0';
398 			else if (ch == ',' || state == 12) {
399 				port = (port << 8) + octet;
400 				state++;
401 			} else
402 				return (0);
403 			break;
404 		}
405 	}
406 
407 	if (state == 13) {
408 		la->true_addr.s_addr = htonl(addr);
409 		la->true_port = port;
410 		return (1);
411 	} else
412 		return (0);
413 }
414 
415 static int
416 ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen)
417 {
418 	char ch, delim;
419 	int i, state;
420 	u_int32_t addr;
421 	u_short port;
422 	u_int8_t octet;
423 
424 	/* Format: "EPRT |1|A.D.D.R|PORT|". */
425 
426 	/* Return if data length is too short. */
427 	if (dlen < 18)
428 		return (0);
429 
430 	if (strncasecmp("EPRT ", sptr, 5))
431 		return (0);
432 
433 	addr = port = octet = 0;
434 	delim = '|';		/* XXX gcc -Wuninitialized */
435 	state = 0;
436 	for (i = 5; i < dlen; i++) {
437 		ch = sptr[i];
438 		switch (state) {
439 		case 0:
440 			if (!isspace(ch)) {
441 				delim = ch;
442 				state++;
443 			}
444 			break;
445 		case 1:
446 			if (ch == '1')	/* IPv4 address */
447 				state++;
448 			else
449 				return (0);
450 			break;
451 		case 2:
452 			if (ch == delim)
453 				state++;
454 			else
455 				return (0);
456 			break;
457 		case 3:
458 		case 5:
459 		case 7:
460 		case 9:
461 			if (isdigit(ch)) {
462 				octet = ch - '0';
463 				state++;
464 			} else
465 				return (0);
466 			break;
467 		case 4:
468 		case 6:
469 		case 8:
470 		case 10:
471 			if (isdigit(ch))
472 				octet = 10 * octet + ch - '0';
473 			else if (ch == '.' || state == 10) {
474 				addr = (addr << 8) + octet;
475 				state++;
476 			} else
477 				return (0);
478 			break;
479 		case 11:
480 			if (isdigit(ch)) {
481 				port = ch - '0';
482 				state++;
483 			} else
484 				return (0);
485 			break;
486 		case 12:
487 			if (isdigit(ch))
488 				port = 10 * port + ch - '0';
489 			else if (ch == delim)
490 				state++;
491 			else
492 				return (0);
493 			break;
494 		}
495 	}
496 
497 	if (state == 13) {
498 		la->true_addr.s_addr = htonl(addr);
499 		la->true_port = port;
500 		return (1);
501 	} else
502 		return (0);
503 }
504 
505 static int
506 ParseFtp227Reply(struct libalias *la, char *sptr, int dlen)
507 {
508 	char ch;
509 	int i, state;
510 	u_int32_t addr;
511 	u_short port;
512 	u_int8_t octet;
513 
514 	/* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
515 
516 	/* Return if data length is too short. */
517 	if (dlen < 17)
518 		return (0);
519 
520 	if (strncmp("227 ", sptr, 4))
521 		return (0);
522 
523 	addr = port = octet = 0;
524 
525 	state = 0;
526 	for (i = 4; i < dlen; i++) {
527 		ch = sptr[i];
528 		switch (state) {
529 		case 0:
530 			if (ch == '(')
531 				state++;
532 			break;
533 		case 1:
534 		case 3:
535 		case 5:
536 		case 7:
537 		case 9:
538 		case 11:
539 			if (isdigit(ch)) {
540 				octet = ch - '0';
541 				state++;
542 			} else
543 				return (0);
544 			break;
545 		case 2:
546 		case 4:
547 		case 6:
548 		case 8:
549 			if (isdigit(ch))
550 				octet = 10 * octet + ch - '0';
551 			else if (ch == ',') {
552 				addr = (addr << 8) + octet;
553 				state++;
554 			} else
555 				return (0);
556 			break;
557 		case 10:
558 		case 12:
559 			if (isdigit(ch))
560 				octet = 10 * octet + ch - '0';
561 			else if (ch == ',' || (state == 12 && ch == ')')) {
562 				port = (port << 8) + octet;
563 				state++;
564 			} else
565 				return (0);
566 			break;
567 		}
568 	}
569 
570 	if (state == 13) {
571 		la->true_port = port;
572 		la->true_addr.s_addr = htonl(addr);
573 		return (1);
574 	} else
575 		return (0);
576 }
577 
578 static int
579 ParseFtp229Reply(struct libalias *la, char *sptr, int dlen)
580 {
581 	char ch, delim;
582 	int i, state;
583 	u_short port;
584 
585 	/* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
586 
587 	/* Return if data length is too short. */
588 	if (dlen < 11)
589 		return (0);
590 
591 	if (strncmp("229 ", sptr, 4))
592 		return (0);
593 
594 	port = 0;
595 	delim = '|';		/* XXX gcc -Wuninitialized */
596 
597 	state = 0;
598 	for (i = 4; i < dlen; i++) {
599 		ch = sptr[i];
600 		switch (state) {
601 		case 0:
602 			if (ch == '(')
603 				state++;
604 			break;
605 		case 1:
606 			delim = ch;
607 			state++;
608 			break;
609 		case 2:
610 		case 3:
611 			if (ch == delim)
612 				state++;
613 			else
614 				return (0);
615 			break;
616 		case 4:
617 			if (isdigit(ch)) {
618 				port = ch - '0';
619 				state++;
620 			} else
621 				return (0);
622 			break;
623 		case 5:
624 			if (isdigit(ch))
625 				port = 10 * port + ch - '0';
626 			else if (ch == delim)
627 				state++;
628 			else
629 				return (0);
630 			break;
631 		case 6:
632 			if (ch == ')')
633 				state++;
634 			else
635 				return (0);
636 			break;
637 		}
638 	}
639 
640 	if (state == 7) {
641 		la->true_port = port;
642 		return (1);
643 	} else
644 		return (0);
645 }
646 
647 static void
648 NewFtpMessage(struct libalias *la, struct ip *pip,
649     struct alias_link *lnk,
650     int maxpacketsize,
651     int ftp_message_type)
652 {
653 	struct alias_link *ftp_lnk;
654 
655 /* Security checks. */
656 	if (pip->ip_src.s_addr != la->true_addr.s_addr)
657 		return;
658 
659 	if (la->true_port < IPPORT_RESERVED)
660 		return;
661 
662 	/* Establish link to address and port found in FTP control message. */
663 	ftp_lnk = AddLink(la, la->true_addr, GetDestAddress(lnk),
664 	    GetAliasAddress(lnk), htons(la->true_port), 0, GET_ALIAS_PORT,
665 	    IPPROTO_TCP);
666 
667 	if (ftp_lnk != NULL) {
668 		int slen, hlen, tlen, dlen;
669 		struct tcphdr *tc;
670 
671 #ifndef NO_FW_PUNCH
672 		/* Punch hole in firewall */
673 		PunchFWHole(ftp_lnk);
674 #endif
675 
676 /* Calculate data length of TCP packet */
677 		tc = (struct tcphdr *)ip_next(pip);
678 		hlen = (pip->ip_hl + tc->th_off) << 2;
679 		tlen = ntohs(pip->ip_len);
680 		dlen = tlen - hlen;
681 
682 /* Create new FTP message. */
683 		{
684 			char stemp[MAX_MESSAGE_SIZE + 1];
685 			char *sptr;
686 			u_short alias_port;
687 			u_char *ptr;
688 			int a1, a2, a3, a4, p1, p2;
689 			struct in_addr alias_address;
690 
691 /* Decompose alias address into quad format */
692 			alias_address = GetAliasAddress(lnk);
693 			ptr = (u_char *) & alias_address.s_addr;
694 			a1 = *ptr++;
695 			a2 = *ptr++;
696 			a3 = *ptr++;
697 			a4 = *ptr;
698 
699 			alias_port = GetAliasPort(ftp_lnk);
700 
701 /* Prepare new command */
702 			switch (ftp_message_type) {
703 			case FTP_PORT_COMMAND:
704 			case FTP_227_REPLY:
705 				/* Decompose alias port into pair format. */
706 				ptr = (char *)&alias_port;
707 				p1 = *ptr++;
708 				p2 = *ptr;
709 
710 				if (ftp_message_type == FTP_PORT_COMMAND) {
711 					/* Generate PORT command string. */
712 					sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
713 					    a1, a2, a3, a4, p1, p2);
714 				} else {
715 					/* Generate 227 reply string. */
716 					sprintf(stemp,
717 					    "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
718 					    a1, a2, a3, a4, p1, p2);
719 				}
720 				break;
721 			case FTP_EPRT_COMMAND:
722 				/* Generate EPRT command string. */
723 				sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
724 				    a1, a2, a3, a4, ntohs(alias_port));
725 				break;
726 			case FTP_229_REPLY:
727 				/* Generate 229 reply string. */
728 				sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n",
729 				    ntohs(alias_port));
730 				break;
731 			}
732 
733 /* Save string length for IP header modification */
734 			slen = strlen(stemp);
735 
736 /* Copy modified buffer into IP packet. */
737 			sptr = (char *)pip;
738 			sptr += hlen;
739 			strncpy(sptr, stemp, maxpacketsize - hlen);
740 		}
741 
742 /* Save information regarding modified seq and ack numbers */
743 		{
744 			int delta;
745 
746 			SetAckModified(lnk);
747 			tc = (struct tcphdr *)ip_next(pip);
748 			delta = GetDeltaSeqOut(tc->th_seq, lnk);
749 			AddSeq(lnk, delta + slen - dlen, pip->ip_hl,
750 			    pip->ip_len, tc->th_seq, tc->th_off);
751 		}
752 
753 /* Revise IP header */
754 		{
755 			u_short new_len;
756 
757 			new_len = htons(hlen +
758 			    MIN(slen, maxpacketsize - hlen));
759 			DifferentialChecksum(&pip->ip_sum,
760 			    &new_len,
761 			    &pip->ip_len,
762 			    1);
763 			pip->ip_len = new_len;
764 		}
765 
766 /* Compute TCP checksum for revised packet */
767 		tc->th_sum = 0;
768 #ifdef _KERNEL
769 		tc->th_x2 = 1;
770 #else
771 		tc->th_sum = TcpChecksum(pip);
772 #endif
773 	} else {
774 #ifdef LIBALIAS_DEBUG
775 		fprintf(stderr,
776 		    "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
777 #endif
778 	}
779 }
780