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