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