1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2001 Charles Mott <[email protected]>
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
fingerprint_out(struct libalias * la,struct alias_data * ah)110 fingerprint_out(struct libalias *la, struct alias_data *ah)
111 {
112
113 if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
114 ah->maxpktsize == 0)
115 return (-1);
116 if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER ||
117 ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
118 return (0);
119 return (-1);
120 }
121
122 static int
fingerprint_in(struct libalias * la,struct alias_data * ah)123 fingerprint_in(struct libalias *la, struct alias_data *ah)
124 {
125
126 if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL)
127 return (-1);
128 if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER ||
129 ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
130 return (0);
131 return (-1);
132 }
133
134 static int
protohandler_out(struct libalias * la,struct ip * pip,struct alias_data * ah)135 protohandler_out(struct libalias *la, struct ip *pip, struct alias_data *ah)
136 {
137
138 AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize);
139 return (0);
140 }
141
142 static int
protohandler_in(struct libalias * la,struct ip * pip,struct alias_data * ah)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
mod_handler(module_t mod,int type,void * data)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
AliasHandleFtpOut(struct libalias * la,struct ip * pip,struct alias_link * lnk,int maxpacketsize)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
AliasHandleFtpIn(struct libalias * la,struct ip * pip,struct alias_link * lnk)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
ParseFtpPortCommand(struct libalias * la,char * sptr,int dlen)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
ParseFtpEprtCommand(struct libalias * la,char * sptr,int dlen)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
ParseFtp227Reply(struct libalias * la,char * sptr,int dlen)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
ParseFtp229Reply(struct libalias * la,char * sptr,int dlen)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
NewFtpMessage(struct libalias * la,struct ip * pip,struct alias_link * lnk,int maxpacketsize,int ftp_message_type)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 +
756 MIN(slen, maxpacketsize - hlen));
757 DifferentialChecksum(&pip->ip_sum,
758 &new_len,
759 &pip->ip_len,
760 1);
761 pip->ip_len = new_len;
762 }
763
764 /* Compute TCP checksum for revised packet */
765 tc->th_sum = 0;
766 #ifdef _KERNEL
767 tc->th_x2 = 1;
768 #else
769 tc->th_sum = TcpChecksum(pip);
770 #endif
771 } else {
772 #ifdef LIBALIAS_DEBUG
773 fprintf(stderr,
774 "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
775 #endif
776 }
777 }
778