2014년 5월 12일 월요일

TCP/IP Stack for Embedded MCU (atmega128)


Original Article : http://blog.naver.com/simonsayz/120212895522

 
코드는 제가...
The time to share with the pascal ... :)
CPU : Atmega128 tested
 
//******************************************************************************
//
//  ** **** PPP Driver
//
//  2011.08.08 Rcv오류 수정 (-20 대신 xor 사용)
//
//
//  -------------------------------------------------------------------------
//
//  API
//  -------------------------------------------------------------------------
//
//  1. Function PPP_Open ('s=2')
//  2. Function PPP_Close;
//  3. Function Svr_Open (TCP,80,'
www.maxpaper.com' )
//  4. Function Svr_Close;
//  5. Function Svr_Snd  (
//  6. Function Svr_Rcv  (
//
//
//
//  To Do.
//
//    1. 접속,해제 관련 State 정리
//
//
//
//  중요한 부분
//   1. IPCP 의 경우, Address 0.0.0.0 의 Request에 대하여, Nak을 주면서,
//                    사용자 PC의 IP를 줌
//                    기본적으로는 서버 IP를 되돌려줌.
//
//  To Do
//  -------------------------------------------------------------------------
//
//
//  Ref.
//   #1. Intenet
//      
http://www.apps.ietf.org/rfc/rfc1331.html
//       http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/TCP_IP/IP_Header
//
//   #2. Book
//         PPP Design Implementation and Debugging 2nd Ed
//           p57       A Request ID 1
//                     B Reject  ID 1
//                     A Request ID 2
//                     B Nak     ID 2
//                     A Request ID 3
//                     B Ack     ID 3
//                     B Request ID 1
//                     A Ack     ID 1
//
//
//******************************************************************************

unit PPP_Drv;
interface
Uses
 StdCtrls,SysUtils,Windows,VCL_Uart,Forms;

Const
 _cFCS_Init                 = $ffff; // Initial FCS value
 //
 _cPPPFlag                  =   $7E;
 _cEscape                   =   $7D; // 111:1101 -> 뒷 문자 X - $20
 _cMaxBuf                   =  1600;
 _cMaxOpts                  =     7;
 // RFC 1331
 _cPktLCP                   = $C021;
 _cPktCHAP                  = $C223;
 _cPktIPCP                  = $8021;
 _cPktCTCP                  = $002D;
 _cPktUTCP                  = $002F;
 // LCP,IPCP
 _cConf_Req                 =     1;
 _cConf_Ack                 =     2;
 _cConf_Nak                 =     3;
 _cConf_Rej                 =     4;
 _cTerm_Req                 =     5;
 _cTerm_Ack                 =     6;
 _cCode_Rej                 =     7;
 _cProt_Rej                 =     8;
 _cEcho_Req                 =     9;
 _cEcho_Rep                 =    10;
 _cDisc_Req                 =    11;
 _cReserved                 =    12;
 //
 _cLCP_MRU                  =     1; // Maximum_Receive_Unit
 _cLCP_ACCM                 =     2; // Asynchronous Control Character Map
 _cLCP_Auth                 =     3; // Authentication_Protocol
 _cLCP_Quality              =     4; // Quality_Protocol
 _cLCP_Magic                =     5; // Magic_Number
 _cLCP_PFC                  =     7; // Protocol_Field_Compression
 _cLCP_ACFC                 =     8; // Address_Control_Field_Compression
 _cLCP_MLP                  =    12; // Deprecated (Multi-Link-Procedure)
 _cLCP_CallBack             =    13; // $0D CallBack RFC1570
 //
 _cIPCP_IPComp              =     2; // L:6 IP_Compress_Protocol
 _cIPCP_IPAddr              =     3; // L:6 IP_Address
 _cIPCP_1DNSAddr            =   $81; // L:6 Primary_DNS_Addr
 _cIPCP_1NBNSAddr           =   $82; // L:6 Primary_NBNS_Addr
 _cIPCP_2DNSAddr            =   $83; // L:6 Secondary_DNS_Addr
 _cIPCP_2NBNSAddr           =   $84; // L:6 Secondary_NBNS_Addr
 //
 _cIP_TCP                   =   $06;
 _cIP_UDP                   =   $11;
 //
 _cProto_DNS                =   $35;
 _cProto_Web                =   $50;
 //
 _cURG                      =   $20;
 _cACK                      =   $10;
 _cPSH                      =   $08;
 _cRST                      =   $04;
 _cSYN                      =   $02;
 _cFIN                      =   $01;


Type
 //
 TStepState     = (ns0_Init,       // 초기 상태
                   ns1_LCP_Req,    // 서버 -> PC
                   nsLCP_1Rcv,     // 첫번째 LCP 읽고,
                   nsLCP_1Ack,     // Ack 보내고,
                   nsIPCP_1Req);
 //
 TBuf4          = Packed Array[0.. 3] of Byte;
 TBufTcpIp      = Packed Array[0..39] of Byte; // Header
 TStr           = String[60];
 TFCSWork       = (fwStart,
                   fwWork,
                   fwEnd);
 TCodeState     = (csCmd,
                   csLen,
                   csDat);
 TCRCType       = (ctTCP,
                   ctIP,
                   ctUDP);
 TTcpState      = (tsOpen,       // Flag 2 UAPRSF (Sync)
                   tsWork,       // Flag
                   tsClose);
 //
 TPktType       = (ptNone,
                   ptLCP,        // Frame Packet Type
                   ptCHAP,
                   ptIPCP,
                   ptTCP);
 TPktState      = (psStart,      // Frame State
                   psAddr,
                   psControl,
                   psProtocol,
                   psData,
                   psFCS,
                   psStop);
 TPktData       = Packed Record  // Frame Data
                   Len         : Word;
                   Data        : Array[0.._cMaxBuf-1] of Byte;
                  End;
 //
 TIPCP_Cmd      = (icUnknown,    //
                   icIP_Addr,    // IP Addr
                   icIP_Comp,
                   icIP_DNS1,
                   icIP_DNS2);
 TOpt           = Record
                   Cmd : Byte;
                   Len : Byte;
                   Dat : TBuf4;
                  End;
 TOpts          = Record
                   Cnt : Integer;
                   DB  : Array[0.._cMaxOpts-1] of TOpt;
                  End;
 //
 TState_Frame   = (sfCode,
                   sfID,
                   sfLen,
                   sfData);
 //
 TState_TcpIp   = (stVerLen,     // IP  ------------
                   stTOS,
                   stLen,
                   stID,
                   stFlagIP,
                   stTTL,
                   stProtocol,
                   stChkSumIP,
                   stSrcIP,
                   stDstIP,
                   stSrcPort,    // TCP ------------
                   stDstPort,
                   stSeqNum,
                   stAck,
                   stHLen,
                   stFlagTCP,
                   stWin,
                   stChkSumTCP,
                   stUrgent,
                   stData);      // Data -----------
 //
 TPktFrame      = Record
                   State       : TState_Frame;
                   Code        : Byte;
                   ID          : Byte;
                   Len         : Word;     // Code + ID + Len + Data (ex. Data : 16-> 20)
                   Opts        : TOpts;
                  End;
 TPktTcpIp      = Record
                   State       : TState_TCPIP;
                   // IP ------------------------------------------------------
                   VerLen      : Byte;     //  1 $45            : IPV4 / IHL : 5 x 32b = 20 Bytes
                   TOS         : Byte;     //  2 $00            : Type of Service
                   Length      : Word;     //  4 $00,$2C [44]   : Ver ~ Data (TCP+ Data)
                   ID          : Word;     //  6 $73,$49
                   FlagIP      : Word;     //  7 $40,$00        : 0100:0000
                   TTL         : Byte;     //  9 $40,           :
                   Protocol    : Byte;     // 10 $06,           : TCP , $11 : UDP
                   ChkSumIP    : Word;     // 12 $74,$74
                   SrcIP       : TBuf4;    // 16 $0A,$17,$06,$04
                   DstIP       : TBuf4;    // 20 $7C,$D9,$C6,$1A
                   // TCP -----------------------------------------------------
                   SrcPort     : Word;     //  2 $5C,$7A
                   DstPort     : Word;     //  4 $00,$50
                   SegNum      : TBuf4;    //  8 $B1,$14,$19,$B2
                   Ack         : TBuf4;    // 12 $DE,$AC,$94,$D1
                   HLen        : Byte;     // 13 $60 [0110:0000 ->
                   FlagTCP     : Byte;     // 14 $02  UAPRSF ->Sync
                   Win         : Word;     // 16 $01,$FF
                   ChkSumTCP   : Word;     // 18 $AC,$EB,
                   Urgent      : Word;     // 20 $00,$00
                  End;
 TPktDNS        = Record
                   ID          : Word; //
                   Flags       : Word; // 0100  Recursion Desired
                   QuestionRR  : Word; // 0001 Question 1
                   AnswerRR    : Word; // 0000 Query시 0
                   AuthorityRR : Word; // 0000 Query시 0
                   AdditionRR  : Word; // 0000 Query시 0
                   // Addr     : String[50];
                   QueryType   : Word; // 0001 : IP
                   QueryClass  : Word; // 0001 : Internet
                   Name        : Word; // C00C : Name
                   TypeRR      : Word; // 0001 : Type / A
                   //ClassRR   : Word; // 0001 : Class / In
                   TTL         : TBuf4;// 00000000 :
                   Length      : Word; // 0004 : IP경우 4
                   //IP        : TBuf4;//        IP Address
                  End;
{
         //
         $01,$EF, // 02  ID
         $81,$80, // 04  Flag   1000:0001
         $00,$01, // 06  Question Rec
         $00,$01, // 08  Answer Rec
         $00,$05, // 10  Authority Rec
         $00,$05, // 12  Additonal Rec
         $03,$77,$77,$77,                     // Question www
         $08,$6D,$61,$78,$70,$61,$70,$65,$72, //          maxpaper
         $03,$63,$6F,$6D,$00,                 //           com
         $00,$01,                             // Query    Type
         $00,$01,                             // Query    Class
         $C0,$0C,                             // 06 Name --------------------
         $00,$01,                             // 02 Type A
         $00,$01,                             // 02 Clasee / In
         $00,$00,$12,$C1,                     // 04 TTL
         $00,$04,                             // Length
         $7C,$D9,$C6,$1A,                     // Addr
}                                              // ...

 TEnv           = Record
                   ID          : Byte;
                   MagicCode   : TBuf4;
                   IPServer    : TBuf4;    //
                   IPClient    : TBuf4;    // My IP
                   IPHost      : TBuf4;    // Host IP
                   IPComp      : TBuf4;
                   DNS1        : TBuf4;
                   DNS2        : TBuf4;
                   TCPState    : TTCPState;
                  End;
 TPkt           = Record
                   iEscaped    : Boolean;  // 내부 변수
                   iInx        : Word;     // 내부 변수
                   iBuf        : TBuf4;    //
                   iBufTCP     : TBufTcpIp;// TCP/IP Header
                   iFCS        : Word;     // 계산 FCS
                   rFCS        : Word;     // 실제 FCS
                   iStep       : TStepState;

                   //
                   Env         : TEnv;
                   //
                   Type_       : TPktType;
                   State       : TPktState;
                   // PPP Packet
                   Frame       : TPktFrame;
                   TCPIP       : TPktTcpIp;
                   DNS         : TPktDNS;
                   //
                   Data        : TPktData;
                  End;

Var
 _gPkt  : TPkt;       // 패킷 정보
 //
 _gDbg  : TMemo;      // Debug
 _gDStr : String;     // Debug String
 _gUart : TUart;      // Hw


 // Dev Function
 Procedure hwInit        (Uart  : TUart; Dbg : TMemo);
 Procedure hwSndEsc      (sByte : Byte  ); // PPP Escape
 Procedure hwSnd         (sByte : Byte  ); // PPP Direct
 Procedure hwSndBuf      (sData : Pointer; sSize : Integer);

 // Utility Function
 Procedure Dbg            (dsStr : String);
 Function  B2H            (ub  : Byte ) : String;
 Function  B2Buf4         (uA,uB,uC,uD : Byte ) : TBuf4;
 Function  IsSet          (inB : TBuf4) : Boolean;
 Function  B4ToDW         (Buf : TBuf4 ) : DWord;
 Function  DWToB4         (dB : DWord) : TBuf4;

 //
 Procedure IPCP_Data2Opts (Var Pkt : TPkt);
 Procedure IPCP_Opts2Env  (ForceEdit : Boolean; Var Pkt : TPkt);
 Function  IPCP_OptsExist (Var Pkt : TPkt; Cmd : Byte) : Boolean;
 Function  IPCP_OptsComp  (Var Pkt : TPkt; Cmd : Byte; Buf4 : TBuf4) : Boolean;
 Procedure IPCP_Env2Data  (Var Pkt : TPkt);
 Procedure IPCP_Env2DataReject(Var Pkt : TPkt);
 //
 Function  DNS_Query      (Var Pkt : TPkt; Host : TStr) : Boolean;
 Function  DNS_IpPos      (Const Buf : TPktData) : Word;

 //
 Procedure TCP_Send       (Var Pkt : TPkt);
 Procedure TCP_Open       (Var Pkt : TPkt; Ip : TBuf4; Port : Word;Flag : Byte);
 Procedure TCP_Reply      (Var Pkt : TPkt);
 //
 Procedure ppp_Init       (Var Pkt : TPkt);
 Function  ppp_Receive    (Var Pkt : TPkt; InData : Byte) : Boolean;
 //
 Function  ppp_process    (Var Pkt : TPkt; InData : Byte) : Boolean;
 //
 Procedure Send_IPCP_Req;

Const
 //
 _cLCP_Req  : Packed Array[0..55] of Byte =
               ($7E,                                            // Start
                $FF,                                            //  Address
                $7D,$23,                                        //  Control
                $C0,$21,                                        //  Protocol : LCP
                $7D,$21,                                        //  Code : Request
                $7D,$21,                                        //  ID   : 1
                $7D,$20,$7D,$38,                                //  Len  : 18
                $7D,$21,$7D,$24,$7D,$25,$AC,                    //   MRU   01 04 05 AC
                $7D,$22,$7D,$26,$7D,$20,$7D,$20,$7D,$20,$7D,$20,//   ACCM  02 06 00 00 00 00
                $7D,$25,$7D,$26,$7D,$26,$7D,$29,$7D,$22,$7D,$20,//   Magic 05 06 06 09 02 00
                $7D,$27,$7D,$22,                                //   PFC   02 02 (C0 21)
                $7D,$28,$7D,$22,                                //   ACFC  08 02 (FF 03)
                $67,$61,                                        // FCS : Field Check Sequence
                $7E);                                           // End

  _cChap_Res : Packed Array [0..47] of Byte =
               ($7e,
                $c2,$23,                         // CHAP
                $02,                             // Response
                $01,                             // ID
                $00,$2a,                         // (02) Len (42)
                $10,                             // (01) Value-Size (16)
                $95,$38,$af,$c3,$e0,$e0,$7a,$8c, // (16) Option
                $8d,$94,$8e,$43,$ed,$bd,$fd,$3a,
                $30,$31,$30,$35,$37,$34,$30,$31, // (21)
01057401886@lgt.co.kr
                $38,$38,$36,$40,$6c,$67,$74,$2e,
                $63,$6f,$2e,$6b,$72,
                $72,$b6,                         // (02) CRC
                $7e);

 _cIPCP_Req : Packed Array[0..39] of Byte =
               ($7E,                                            // Start
                $FF,                                            //  Address
                $7D,$23,                                        //  Control
                $80,$21,                                        //  Protocol : IPCP
                $7D,$21,                                        //  Code     : Req
                $7D,$30,                                        //  ID       : 10
                $7D,$20,$7D,$30,                                //  Len      : 18 ( Code ~ Data )
                $7D,$22,$7D,$26,$7D,$20,$2D,$7D,$2F,$7D,$21,    //  IP Compress Protocol
                $7D,$23,$7D,$26,$7D,$20,$7D,$20,$7D,$20,$7D,$20,//  IP Addr
                $E3,$73,                                        // FCS
                $7E);                                           // End

 _cIPCP_Req2: Packed Array[0..28] of Byte =
               ($7E,                                            // Start
                $FF,                                            //  Address
                $7D,$23,                                        //  Control
                $80,$21,                                        //  Protocol : IPCP
                $7D,$21,                                        //  Code     : Req
                $7D,$30,                                        //  ID       : 10
                $7D,$20,$7D,$2A,                                //  Len      : 10 ( Code ~ Data )
                $7D,$23,$7D,$26,$7D,$20,$7D,$20,$7D,$20,$7D,$20,//  IP Addr
                $7A,$9A,                                        // FCS
                $7E);                                           // End

implementation
Uses
 PPP_Help;

Const
 _cFCSTbl  : Array[0..255] of Word =
                               ($0000,$1189,$2312,$329b,$4624,$57ad,$6536,$74bf,
                                $8c48,$9dc1,$af5a,$bed3,$ca6c,$dbe5,$e97e,$f8f7,
                                $1081,$0108,$3393,$221a,$56a5,$472c,$75b7,$643e,
                                $9cc9,$8d40,$bfdb,$ae52,$daed,$cb64,$f9ff,$e876,
                                $2102,$308b,$0210,$1399,$6726,$76af,$4434,$55bd,
                                $ad4a,$bcc3,$8e58,$9fd1,$eb6e,$fae7,$c87c,$d9f5,
                                $3183,$200a,$1291,$0318,$77a7,$662e,$54b5,$453c,
                                $bdcb,$ac42,$9ed9,$8f50,$fbef,$ea66,$d8fd,$c974,
                                $4204,$538d,$6116,$709f,$0420,$15a9,$2732,$36bb,
                                $ce4c,$dfc5,$ed5e,$fcd7,$8868,$99e1,$ab7a,$baf3,
                                $5285,$430c,$7197,$601e,$14a1,$0528,$37b3,$263a,
                                $decd,$cf44,$fddf,$ec56,$98e9,$8960,$bbfb,$aa72,
                                $6306,$728f,$4014,$519d,$2522,$34ab,$0630,$17b9,
                                $ef4e,$fec7,$cc5c,$ddd5,$a96a,$b8e3,$8a78,$9bf1,
                                $7387,$620e,$5095,$411c,$35a3,$242a,$16b1,$0738,
                                $ffcf,$ee46,$dcdd,$cd54,$b9eb,$a862,$9af9,$8b70,
                                $8408,$9581,$a71a,$b693,$c22c,$d3a5,$e13e,$f0b7,
                                $0840,$19c9,$2b52,$3adb,$4e64,$5fed,$6d76,$7cff,
                                $9489,$8500,$b79b,$a612,$d2ad,$c324,$f1bf,$e036,
                                $18c1,$0948,$3bd3,$2a5a,$5ee5,$4f6c,$7df7,$6c7e,
                                $a50a,$b483,$8618,$9791,$e32e,$f2a7,$c03c,$d1b5,
                                $2942,$38cb,$0a50,$1bd9,$6f66,$7eef,$4c74,$5dfd,
                                $b58b,$a402,$9699,$8710,$f3af,$e226,$d0bd,$c134,
                                $39c3,$284a,$1ad1,$0b58,$7fe7,$6e6e,$5cf5,$4d7c,
                                $c60c,$d785,$e51e,$f497,$8028,$91a1,$a33a,$b2b3,
                                $4a44,$5bcd,$6956,$78df,$0c60,$1de9,$2f72,$3efb,
                                $d68d,$c704,$f59f,$e416,$90a9,$8120,$b3bb,$a232,
                                $5ac5,$4b4c,$79d7,$685e,$1ce1,$0d68,$3ff3,$2e7a,
                                $e70e,$f687,$c41c,$d595,$a12a,$b0a3,$8238,$93b1,
                                $6b46,$7acf,$4854,$59dd,$2d62,$3ceb,$0e70,$1ff9,
                                $f78f,$e606,$d49d,$c514,$b1ab,$a022,$92b9,$8330,
                                $7bc7,$6a4e,$58d5,$495c,$3de3,$2c6a,$1ef1,$0f78);

//*****************************************************************************
//
//  HardWare Driver Function
//  -------------------------------------------------------------------------
//  추후 HW 환경에 맞도록 Layer 분리
//
//
//*****************************************************************************

// Dev Function
Procedure hwInit  (Uart  : TUart; Dbg : TMemo);
 begin
  //
  _gUart := Uart;
  _gDbg  := Dbg;
  _gDStr := '';
 end;

// PPP Escape
Procedure hwSndEsc(sByte : Byte);
 begin
  Case (sByte in [$0..$20,$7d,$7e]) of
   True  : Begin
            _gUart.SendByte($7D          );
            _gUart.SendByte(sByte xor $20);
            _gDStr := _gDStr + B2H($7D)+',$' + B2H(sByte xor $20) + ',$';
           End;
   False : Begin
            _gUart.SendByte(sByte);
            _gDStr := _gDStr + B2H(sByte) + ',$';
           End;
  End;
 end;

// PPP Direct
Procedure hwSnd   (sByte : Byte);
 begin
  _gUart.SendByte(sByte);
  _gDStr := _gDStr + B2H(sByte) + ',$';
 end;

//
Procedure hwSndBuf      (sData : Pointer; sSize : Integer);
 begin
  _gUart.SendData(sData,sSize);
 end;

//*****************************************************************************
//
//  Utility Function
//  -------------------------------------------------------------------------
//
//
//*****************************************************************************

//
Procedure Dbg(dsStr : String);
 begin
  _gDbg.Lines.Add(dsStr);
 end;

//
Function  B2H (ub : Byte) : String;
 begin
  Result := IntToHex(ub,2);
 end;

//
Function  W2H (uW : Word) : String;
 begin
  Result := IntToHex(uW,4);
 end;

//
Function IsSet(inB : TBuf4) : Boolean;
 begin
  Result := (inB[0] + inB[1] + inB[2] + inB[3]) > 0;
 end;

//
Function B4ToDW(Buf : TBuf4 ) : DWord;
 begin
  Result := (Buf[0] shl 24) or
            (Buf[1] shl 16) or
            (Buf[2] shl  8) or
            (Buf[3] shl  0);
 end;

//
Function DWToB4(dB : DWord) : TBuf4;
 begin
  Result[0] := (db shr 24) and $FF;
  Result[1] := (db shr 16) and $FF;
  Result[2] := (db shr  8) and $FF;
  Result[3] := (db shr  0) and $FF;
 end;

//
Function  B2Buf4(uA,uB,uC,uD : Byte ) : TBuf4;
 begin
  Result[0] := uA;
  Result[1] := uB;
  Result[2] := uC;
  Result[3] := uD;
 end;

// FCS [Protocol ~ Data]
Procedure hwSndCRC(fcIn : Byte; Work  : TFCSWork; Var FCS : Word);
 Begin
  //
  Case fcIn of
   _cPPPFlag : begin
                hwSnd($7D);
                hwSnd(fcIn xor $20);
               end;
   else        hwSnd(fcIn);
  End;
  //
  Case Work of
   fwStart,
   fwWork   : begin
               If Work = fwStart then FCS := _cFCS_Init;
               FCS := (FCS shr 8) xor _cFCSTbl[(FCS xor fcIn) and $FF];
              end;
   fwEnd    :  FCS := Swap((FCS xor $FFFF));
  End;
 End;

// FCS [Protocol ~ Data]
Procedure CRC_PPP(Work  : TFCSWork; fcIn : Byte; Var FCS : Word);
 Begin
  //
  Case Work of
   fwStart,
   fwWork   : begin
               If Work = fwStart then FCS := _cFCS_Init;
               FCS := (FCS shr 8) xor _cFCSTbl[(FCS xor fcIn) and $FF];
              end;
   fwEnd    :  FCS := Swap((FCS xor $FFFF));
  End;
 End;


// Data : IP 첫번쨰 위치
// IpCSum := CRC_IP(@_cExIP);
Function CRC_IP  (data:pointer):word;
var
 pw   : Pword;
 x    : word;
 csum : longint;

Begin
 csum := 0;
 //
 pw := data;
 For x := 1 to 10 do
  Begin
   csum := csum + pw^;
   inc(pw);
  end;
 //
 csum   := (csum and $ffff) + (csum shr 16);
 csum   := csum + (csum shr 16);
 csum   := (csum and $00ff) shl 8 + (csum shr 8);
 Result := csum xor $FFFF;
end;

// Pseudo IP Sum
Function CRC_PseudoIP_Sum(pIP : Pointer) : LongInt;
 Var
  pw  : PWord;
  X   : Byte;
 Begin
  // 초기화
  Result := 0;
  // IP Sum
  pw := pIP;
  For x := 1 to 6 do
   Begin
    Result := Result + pw^;
    inc(pw);
   End;
 End;

// TCP :  CRC_TcpUdp(cSum, Pkt, 20);
// UDP :  CRC_TcpUdp(cSum, Pkt,  8);
Function CRC_TcpUdpCalc(cSum    : LongInt;
                        Var Pkt : TPkt; HdrLen : Integer) : Word;
var
 pw    : Pword;
 x     : word;
Begin
 // Step #01. Header (IP+TCP,IP+UDP) ------------------------------------------
 pw := @Pkt.iBufTCP;
 For x := 1 to (HdrLen shr 1) do
  Begin
   csum := csum + pw^;
   inc(pw);
  End;
 // Step #02. Data  -----------------------------------------------------------
 pw := @Pkt.Data.Data;
 For x := 1 to (Pkt.Data.Len shr 1) do
  Begin
   csum := csum + pw^;
   inc(pw);
  End;

 If (Pkt.Data.Len mod 2=1) then
  Begin
   X    := PByte(pw)^;
   csum := csum + X;
  end;

 csum   := (csum and $ffff) + (csum shr 16);
 csum   := csum + (csum shr 16);
 csum   := (csum and $00ff) shl 8 + (csum shr 8);
 Result := csum xor $FFFF;
end;

// 전제 조건 : TCPIP ,Data.Len 값이 정확해야 함
Function CRC_TcpUdp(Var Pkt : TPkt; CrcType : TCRCType) : Word;
 var
  IpSum : LongInt;
 begin
  //
  Case CrcType of
   ctIP  : begin
            // IP
            Pkt.iBufTCP[ 0] :=     Pkt.TCPIP.VerLen;
            Pkt.iBufTCP[ 1] :=     Pkt.TCPIP.TOS;
            Pkt.iBufTCP[ 2] :=  Hi(Pkt.TCPIP.Length);
            Pkt.iBufTCP[ 3] :=  Lo(Pkt.TCPIP.Length);
            Pkt.iBufTCP[ 4] :=  Hi(Pkt.TCPIP.ID    );
            Pkt.iBufTCP[ 5] :=  Lo(Pkt.TCPIP.ID    );
            Pkt.iBufTCP[ 6] :=  Hi(Pkt.TCPIP.FlagIP);
            Pkt.iBufTCP[ 7] :=  Lo(Pkt.TCPIP.FlagIP);
            Pkt.iBufTCP[ 8] :=     Pkt.TCPIP.TTL;
            Pkt.iBufTCP[ 9] :=     Pkt.TCPIP.Protocol;
            Pkt.iBufTCP[10] :=  0; // ChkSumIP
            Pkt.iBufTCP[11] :=  0; // ChkSumIP
            Pkt.iBufTCP[12] :=     Pkt.TCPIP.SrcIP[0];
            Pkt.iBufTCP[13] :=     Pkt.TCPIP.SrcIP[1];
            Pkt.iBufTCP[14] :=     Pkt.TCPIP.SrcIP[2];
            Pkt.iBufTCP[15] :=     Pkt.TCPIP.SrcIP[3];
            Pkt.iBufTCP[16] :=     Pkt.TCPIP.DstIP[0];
            Pkt.iBufTCP[17] :=     Pkt.TCPIP.DstIP[1];
            Pkt.iBufTCP[18] :=     Pkt.TCPIP.DstIP[2];
            Pkt.iBufTCP[19] :=     Pkt.TCPIP.DstIP[3];
            //
            Result  := CRC_IP  (@Pkt.iBufTCP);
           end;
   ctTCP,
   ctUDP : begin
            // Calc Pseudo Sum
            Pkt.iBufTCP[ 0] :=     Pkt.TCPIP.SrcIP[0];
            Pkt.iBufTCP[ 1] :=     Pkt.TCPIP.SrcIP[1];
            Pkt.iBufTCP[ 2] :=     Pkt.TCPIP.SrcIP[2];
            Pkt.iBufTCP[ 3] :=     Pkt.TCPIP.SrcIP[3];
            Pkt.iBufTCP[ 4] :=     Pkt.TCPIP.DstIP[0];
            Pkt.iBufTCP[ 5] :=     Pkt.TCPIP.DstIP[1];
            Pkt.iBufTCP[ 6] :=     Pkt.TCPIP.DstIP[2];
            Pkt.iBufTCP[ 7] :=     Pkt.TCPIP.DstIP[3];
            Pkt.iBufTCP[ 8] :=     0; // Pad
            Pkt.iBufTCP[ 9] :=     Pkt.TCPIP.Protocol;
            // TCP
            Case CrcType of
             ctTCP : begin
                      Pkt.iBufTCP[10] :=  Hi(20 + Pkt.Data.Len); // TCP + Data Length
                      Pkt.iBufTCP[11] :=  Lo(20 + Pkt.Data.Len); // TCP + Data Length
                      IpSum   := CRC_PseudoIP_Sum(@Pkt.iBufTCP);
                      //
                      Pkt.iBufTCP[ 0] :=  Hi(Pkt.TCPIP.SrcPort);
                      Pkt.iBufTCP[ 1] :=  Lo(Pkt.TCPIP.SrcPort);
                      Pkt.iBufTCP[ 2] :=  Hi(Pkt.TCPIP.DstPort);
                      Pkt.iBufTCP[ 3] :=  Lo(Pkt.TCPIP.DstPort);
                      Pkt.iBufTCP[ 4] :=     Pkt.TCPIP.SegNum[0];
                      Pkt.iBufTCP[ 5] :=     Pkt.TCPIP.SegNum[1];
                      Pkt.iBufTCP[ 6] :=     Pkt.TCPIP.SegNum[2];
                      Pkt.iBufTCP[ 7] :=     Pkt.TCPIP.SegNum[3];
                      Pkt.iBufTCP[ 8] :=     Pkt.TCPIP.Ack   [0];
                      Pkt.iBufTCP[ 9] :=     Pkt.TCPIP.Ack   [1];
                      Pkt.iBufTCP[10] :=     Pkt.TCPIP.Ack   [2];
                      Pkt.iBufTCP[11] :=     Pkt.TCPIP.Ack   [3];
                      Pkt.iBufTCP[12] :=     Pkt.TCPIP.HLen;
                      Pkt.iBufTCP[13] :=     Pkt.TCPIP.FlagTCP;
                      Pkt.iBufTCP[14] :=  Hi(Pkt.TCPIP.Win    );
                      Pkt.iBufTCP[15] :=  Lo(Pkt.TCPIP.Win    );
                      Pkt.iBufTCP[16] :=  0; // TCP Check Sum
                      Pkt.iBufTCP[17] :=  0;
                      Pkt.iBufTCP[18] :=  Hi(Pkt.TCPIP.Urgent );
                      Pkt.iBufTCP[19] :=  Lo(Pkt.TCPIP.Urgent );
                      Result := CRC_TcpUdpCalc(IpSum,Pkt,20);
                     end;
             ctUDP : begin
                      Pkt.iBufTCP[10] :=  Hi(8 + Pkt.Data.Len); // TCP + Data Length
                      Pkt.iBufTCP[11] :=  Lo(8 + Pkt.Data.Len); // TCP + Data Length
                      IpSum   := CRC_PseudoIP_Sum(@Pkt.iBufTCP);
                      //
                      Pkt.iBufTCP[ 0] :=  Hi(Pkt.TCPIP.SrcPort);
                      Pkt.iBufTCP[ 1] :=  Lo(Pkt.TCPIP.SrcPort);
                      Pkt.iBufTCP[ 2] :=  Hi(Pkt.TCPIP.DstPort);
                      Pkt.iBufTCP[ 3] :=  Lo(Pkt.TCPIP.DstPort);
                      Pkt.iBufTCP[ 4] :=  Hi(Pkt.TCPIP.Win    );
                      Pkt.iBufTCP[ 5] :=  Lo(Pkt.TCPIP.Win    );
                      Pkt.iBufTCP[ 6] :=  0; // UDP Check Sum
                      Pkt.iBufTCP[ 7] :=  0;
                      Result := CRC_TcpUdpCalc(IpSum,Pkt,8);
                     end;
            End;
            //
           end;
  End;
 end;

// Raw 데이터를 Pkt.Frame.Opts에...
// Pkt.Data -> Pkt.Frame.Opts
Procedure IPCP_Data2Opts(Var Pkt : TPkt);
 Var
  Lp     : Integer;
  CState : TCodeState;
  Dat    : Byte;
  Inx    : Integer;
 begin
  // 초기화
  cState             := csCmd;
  Inx                := 0;
  Pkt.Frame.Opts.Cnt := 0;
  //
  For Lp := 0 to Pkt.Data.Len-1 do
   begin
    Dat := Pkt.Data.Data[Lp];
    //
    Case cState of
     csCmd : Begin
              cState := csLen;
              Pkt.Frame.Opts.DB[Pkt.Frame.Opts.Cnt].Cmd := Dat;
             End;
     csLen : Begin
              cState := csDat;
              Pkt.Frame.Opts.DB[Pkt.Frame.Opts.Cnt].Len := Dat;
              Inx    := 0;
              Case Pkt.Frame.Opts.DB[Pkt.Frame.Opts.Cnt].Len <= 2 of
               True  : Begin
                        cState := csCmd;
                        Inc(Pkt.Frame.Opts.Cnt);
                       End;
               False :  cState := csDat;
              End;
             End;
     csDat : Begin
              //
              Pkt.Frame.Opts.DB[Pkt.Frame.Opts.Cnt].Dat[Inx] := Dat;
              Inc(Inx);
              //
              If Inx >= Pkt.Frame.Opts.DB[Pkt.Frame.Opts.Cnt].Len-2 then
               Begin
                cState := csCmd;
                Inc(Pkt.Frame.Opts.Cnt);
               End;
             End;
    End;
   end;
 end;

// Opts값 -> Env변수 Update
Procedure IPCP_Opts2Env (ForceEdit  : Boolean; Var Pkt : TPkt);
 Var
  Lp : Integer;
 begin
  For Lp := 0 to Pkt.Frame.Opts.Cnt-1 do
   Case Pkt.Frame.Opts.DB[Lp].Cmd of
    _cIPCP_IPComp    : If (ForceEdit)or(Not(IsSet(Pkt.Env.IPComp))) then
                        Pkt.Env.IPComp := Pkt.Frame.Opts.DB[Lp].Dat;
    _cIPCP_IPAddr    : Case ForceEdit of
                        True : Pkt.Env.IPClient := Pkt.Frame.Opts.DB[Lp].Dat;
                        False: Pkt.Env.IPServer := Pkt.Frame.Opts.DB[Lp].Dat;
                       End;
    _cIPCP_1DNSAddr  : If (ForceEdit)or(Not(IsSet(Pkt.Env.DNS1  ))) then
                        Pkt.Env.DNS1   := Pkt.Frame.Opts.DB[Lp].Dat;
    _cIPCP_2DNSAddr  : If (ForceEdit)or(Not(IsSet(Pkt.Env.DNS2  ))) then
                        Pkt.Env.DNS2   := Pkt.Frame.Opts.DB[Lp].Dat;
    _cIPCP_1NBNSAddr : ;
    _cIPCP_2NBNSAddr : ;
   end;
 end;

//
Function  IPCP_OptsExist(Var Pkt : TPkt; Cmd : Byte) : Boolean;
 Var
  Lp : Integer;
 begin
  // 초기화
  Result := False;
  //
  For Lp := 0 to Pkt.Frame.Opts.Cnt-1 do
   If Pkt.Frame.Opts.DB[Lp].Cmd = Cmd then
    begin
     Result := True;
     Exit;
    end;
 end;

//
Function  IPCP_OptsComp (Var Pkt : TPkt; Cmd : Byte; Buf4 : TBuf4) : Boolean;
 Var
  Lp : Integer;
  Lp2: Integer;
 begin
  // 초기화
  Result := False;
  //
  For Lp := 0 to Pkt.Frame.Opts.Cnt-1 do
   If Pkt.Frame.Opts.DB[Lp].Cmd = Cmd then
    begin
     For Lp2 := 0 to 3 do
      If Buf4[Lp2] <> Pkt.Frame.Opts.DB[Lp].Dat[Lp2] then
       Exit;
     //
     Result := True;
     Exit;
    end;
 end;

// Env 변수 -> Pkt.Data로 이전
Procedure IPCP_Env2Data (Var Pkt : TPkt);
 Var
  Ofs : Integer;
 begin
  //
  Ofs := 0;
  Pkt.Data.Data[Ofs+0] := _cIPCP_IPAddr;
  Pkt.Data.Data[Ofs+1] := 6;
  Move(Pkt.Env.IpServer,Pkt.Data.Data[Ofs+2],4);
  //
  Inc(Ofs,6);
  Pkt.Data.Len := Ofs;
 end;

//
Procedure IPCP_Env2DataReject(Var Pkt : TPkt);
 Var
  Ofs : Integer;
 begin
  //
  Ofs := 0;
  //
  Pkt.Data.Data[Ofs+0] := _cIPCP_IPComp;
  Pkt.Data.Data[Ofs+1] := 6;
  Move(Pkt.Env.IpComp,Pkt.Data.Data[Ofs+2],4);
  //
  Inc(Ofs,6);
  Pkt.Data.Data[Ofs+0] := _cIPCP_1DNSAddr;
  Pkt.Data.Data[Ofs+1] := 6;
  Move(Pkt.Env.DNS1,Pkt.Data.Data[Ofs+2],4);
  //
  Inc(Ofs,6);
  Pkt.Data.Data[Ofs+0] := _cIPCP_2DNSAddr;
  Pkt.Data.Data[Ofs+1] := 6;
  Move(Pkt.Env.DNS2,Pkt.Data.Data[Ofs+2],4);
  //
  Inc(Ofs,6);
  Pkt.Data.Len := Ofs;
 end;

//
Procedure Reply_LCP(Var Step : TStepState; Var Pkt : TPkt);
 Var
  FCS : Word;
  Lp  : Integer;
 begin
  _gDStr := '';
  hwSnd   (_cPPPFlag); // 7E
  hwSndEsc($FF   );  CRC_PPP(fwStart,$FF,FCS); // Protocol
  hwSndEsc($03   );  CRC_PPP(fwWork ,$03,FCS);
  hwSndEsc($C0   );  CRC_PPP(fwWork ,$C0,FCS);
  hwSndEsc($21   );  CRC_PPP(fwWork ,$21,FCS);
  Case Pkt.Frame.Code of  // Code
   _cConf_Req : begin
                 Pkt.Frame.Code := _cConf_Ack;
                 hwSndEsc  (Pkt.Frame.Code);
                 CRC_PPP(fwWork,Pkt.Frame.Code,FCS);
                end;
  End;
  hwSndEsc     (Pkt.Frame.ID);      CRC_PPP(fwWork ,Pkt.Frame.ID     ,FCS);
  hwSndEsc     (Hi(Pkt.Frame.Len)); CRC_PPP(fwWork ,Hi(Pkt.Frame.Len),FCS);
  hwSndEsc     (Lo(Pkt.Frame.Len)); CRC_PPP(fwWork ,Lo(Pkt.Frame.Len),FCS);
  // Cmd:Len:Data ...
  For Lp := 0 to Pkt.Data.Len-1 do
   Begin
    hwSndEsc   (Pkt.Data.Data[Lp]);
    CRC_PPP(fwWork ,Pkt.Data.Data[Lp],FCS);
   End;

  CRC_PPP(fwEnd ,0,FCS);
  // FCS
  hwSndEsc(Hi(FCS)  );
  hwSndEsc(Lo(FCS)  );
  hwSnd   (_cPPPFlag);
  // Help...
  // Dbg('Snd:'+_gDStr);
  Help_LCP('Snd:',Pkt );
 end;

//
Procedure Reply_IPCP(Var Step : TStepState; Var Pkt : TPkt);
 Var
  FCS : Word;
  Lp  : Integer;
 begin
  // Raw Data 값을 Opts Record로 이동후, 환경 변수에 저장
  IPCP_Data2Opts(Pkt);
  Case Pkt.Frame.Code of
   _cConf_Req  : IPCP_Opts2Env(False,Pkt);
   _cConf_Nak  : IPCP_Opts2Env(True ,Pkt);
   _cConf_Ack  : Exit;
  End;
  // Help_Env (Pkt);

  _gDStr := '';
  hwSnd   (_cPPPFlag); // 7E
  hwSndEsc($FF      );  CRC_PPP(fwStart,$FF,FCS); // Protocol
  hwSndEsc($03      );  CRC_PPP(fwWork ,$03,FCS);
  hwSndEsc($80      );  CRC_PPP(fwWork ,$80,FCS);
  hwSndEsc($21      );  CRC_PPP(fwWork ,$21,FCS);
  Case Pkt.Frame.Code of  // Code
   _cConf_Req : Begin
                 Dbg('IPCP : Req->Req');
                 If IPCP_OptsExist(Pkt,_cIPCP_1DNSAddr) or
                    IPCP_OptsExist(Pkt,_cIPCP_2DNSAddr) then
                  Begin
                   IPCP_Env2DataReject(Pkt);
                   Pkt.Frame.Len  := Pkt.Data.Len + 4;
                   Pkt.Frame.Code := _cConf_Rej;
                  End
                 else
                  Pkt.Frame.Code := _cConf_Ack;
                 //
                 hwSndEsc  (Pkt.Frame.Code);
                 CRC_PPP(fwWork,Pkt.Frame.Code,FCS);
                End;
   _cConf_Nak : Begin
                 Dbg('IPCP : Nak->Req');
                 Pkt.Frame.Code := _cConf_Req;
                 Inc(Pkt.Env.ID);
                 Pkt.Frame.ID := Pkt.Env.ID;
                 hwSndEsc(Pkt.Frame.Code);
                 CRC_PPP (fwWork,Pkt.Frame.Code,FCS);
                End;
   _cConf_Ack : Begin
                 Pkt.Frame.Code := _cConf_Req;
                 Inc(Pkt.Env.ID);
                 Pkt.Frame.ID := Pkt.Env.ID;

                 hwSndEsc(Pkt.Frame.Code);
                 CRC_PPP (fwWork,Pkt.Frame.Code,FCS);
                end;
  End;
  //
  hwSndEsc     (Pkt.Frame.ID);      CRC_PPP(fwWork ,Pkt.Frame.ID     ,FCS);
  hwSndEsc     (Hi(Pkt.Frame.Len)); CRC_PPP(fwWork ,Hi(Pkt.Frame.Len),FCS);
  hwSndEsc     (Lo(Pkt.Frame.Len)); CRC_PPP(fwWork ,Lo(Pkt.Frame.Len),FCS);
  // Cmd:Len:Data ...
  For Lp := 0 to Pkt.Data.Len-1 do
   Begin
    hwSndEsc   (Pkt.Data.Data[Lp]);
    CRC_PPP(fwWork ,Pkt.Data.Data[Lp],FCS);
   End;

  CRC_PPP(fwEnd ,0,FCS);
  // FCS
  hwSndEsc(Hi(FCS)  );
  hwSndEsc(Lo(FCS)  );
  hwSnd   (_cPPPFlag);
  // Help...
  // Dbg('Snd:'+_gDStr);
  Help_LCP('Snd:', Pkt );
 end;

// Pkt.iFCS : Loop
// Pkt.iInx : 갯수
// Pkt.iBuf : '.' 위치
// 중요한점은 7E는 반드시, Escape 시켜야 함
Function  DNS_Query      (Var Pkt : TPkt; Host : TStr) : Boolean;
 Var
  Lp  : Integer;
  FCS : Word;
 begin
  //
  _gDStr := '';
  Result := False;
  If Host = '' then Exit;
  // DNS 관련 -------------------- 12 + 4 +
  Pkt.DNS.ID            := $01EF;
  Pkt.DNS.Flags         := $0100;
  Pkt.DNS.QuestionRR    := $0001;
  Pkt.DNS.AnswerRR      := $0000;
  Pkt.DNS.AuthorityRR   := $0000;
  Pkt.DNS.AdditionRR    := $0000;
  Pkt.DNS.QueryType     := $0001;
  Pkt.DNS.QueryClass    := $0001;

  // UDP DNS 데이터 설정
  Pkt.Data.Len := 0;
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.ID         ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.ID         ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.Flags      ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.Flags      ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.QuestionRR ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.QuestionRR ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.AnswerRR   ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.AnswerRR   ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.AuthorityRR); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.AuthorityRR); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.AdditionRR ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.AdditionRR ); Inc(Pkt.Data.Len);
  // DNS Name 설정 (
www.maxpaper.com )
  Pkt.iInx := Pkt.Data.Len;
  Pkt.iFCS := 0;
  For Lp := 1 to Length(Host) do
   Case host[Lp] = '.' of
    False : Begin
             Pkt.Data.Data[Pkt.Data.Len+Lp] := Ord(Host[Lp]);
             Inc(Pkt.iFCS);
            End;
    True  : Begin
             Pkt.Data.Data[Pkt.iInx] := Pkt.iFCS;
             Pkt.iInx := Pkt.Data.Len+Lp;
             Pkt.iFCS :=  0;
            End;
   End;
  //
  Pkt.Data.Data[Pkt.iInx] := Pkt.iFCS;  Inc(Pkt.Data.Len,Length(host)+1);
  Pkt.Data.Data[Pkt.Data.Len] := $00;    ;  Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.QueryType  ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.QueryType  ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Hi(Pkt.DNS.QueryClass ); Inc(Pkt.Data.Len);
  Pkt.Data.Data[Pkt.Data.Len] := Lo(Pkt.DNS.QueryClass ); Inc(Pkt.Data.Len);

  // IP 관련
  Pkt.TCPIP.VerLen      := $45;
  Pkt.TCPIP.TOS         := $00;
  Pkt.TCPIP.Length      := 20 + (8 + Pkt.Data.Len); // 62 ( 20 + 8 + 34 )
  Pkt.TCPIP.ID          := $1BA3;
  Pkt.TCPIP.FlagIP      := $0000;
  Pkt.TCPIP.TTL         := $80;
  Pkt.TCPIP.Protocol    := _cIP_UDP;
  Pkt.TCPIP.SrcIp       := Pkt.Env.IPClient; // B2Buf4($0A,$17,$06,$04);
  Pkt.TCPIP.DstIP       := Pkt.Env.DNS1;     // B2Buf4($96,$02,$7E,$66);
  // UDP 관련
  Pkt.TCPIP.SrcPort     := $04EC; // DE6;
  Pkt.TCPIP.DstPort     := $0035; // 53
  Pkt.TCPIP.Win         := 8 + Pkt.Data.Len; // 8 + Data[34]

  // Send ----
  Pkt.TCPIP.ChkSumIP  := CRC_TCPUDP(Pkt,ctIP );
  Pkt.TCPIP.ChkSumTCP := CRC_TCPUDP(Pkt,ctUDP);
  //
  hwSnd (_cPPPFlag); // 7E
  // hwSndEsc(rU,Snd);
  hwSndCRC($FF,fwStart,FCS); // Protocol
  hwSndCRC($03,fwWork ,FCS); // Control
  hwSndCRC($00,fwWork ,FCS); // TCP/IP
  hwSndCRC($21,fwWork ,FCS); //
  // IP -----------------------------------------------------------------------
  hwSndCRC($45                    ,fwWork,FCS); // VerLen
  hwSndCRC($00                    ,fwWork,FCS); // TOS
  hwSndCRC(Hi(Pkt.TCPIP.Length)   ,fwWork,FCS); // Len ( IP + TCP + Data.Len )
  hwSndCRC(Lo(Pkt.TCPIP.Length)   ,fwWork,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.ID    )   ,fwWork,FCS); // ID
  hwSndCRC(Lo(Pkt.TCPIP.ID    )   ,fwWork,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.FlagIP)   ,fwWork,FCS); // FlagIP (3:13)
  hwSndCRC(Lo(Pkt.TCPIP.FlagIP)   ,fwWork,FCS); //
  hwSndCRC(   Pkt.TCPIP.TTL       ,fwWork,FCS); // TTL
  hwSndCRC(   Pkt.TCPIP.Protocol  ,fwWork,FCS); // Protocol
  hwSndCRC(Hi(Pkt.TCPIP.ChkSumIP) ,fwWork,FCS); // IP Check Sum
  hwSndCRC(Lo(Pkt.TCPIP.ChkSumIP) ,fwWork,FCS); // IP Check Sum
  hwSndCRC(   Pkt.TCPIP.SrcIP[0]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[1]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[2]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[3]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[0]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[1]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[2]  ,fwWork,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[3]  ,fwWork,FCS); // SrcIP
  // UDP ----------------------------------------------------------------------
  hwSndCRC(Hi(Pkt.TCPIP.SrcPort  ),fwWork,FCS); // Src Port
  hwSndCRC(Lo(Pkt.TCPIP.SrcPort  ),fwWork,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.DstPort  ),fwWork,FCS); // Dst Port
  hwSndCRC(Lo(Pkt.TCPIP.DstPort  ),fwWork,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.Win      ),fwWork,FCS); // UDP Len
  hwSndCRC(Lo(Pkt.TCPIP.Win      ),fwWork,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.ChkSumTCP),fwWork,FCS); // ChkSum
  hwSndCRC(Lo(Pkt.TCPIP.ChkSumTCP),fwWork,FCS); //
  // TCP Data
  If Pkt.Data.Len > 0 then
   For Lp := 0 to Pkt.Data.Len-1 do
    hwSndCRC(Pkt.Data.Data[Lp],fwWork,FCS);
  //
  CRC_PPP(fwEnd ,0,FCS);
  // FCS
  hwSnd(Hi(FCS)  );
  hwSnd(Lo(FCS)  );
  hwSnd(_cPPPFlag);
  // Help...
  // Dbg('Snd:'+_gDStr);
  Help_TCP('Snd:',Pkt);
 end;

//
Function  DNS_IpPos (Const Buf : TPktData) : Word;
 Var
  Lp    : Integer;
  Cnt   : Integer;
  Inx   : integer;
  DType : Word;
  DLen  : Word;

begin
 //
 Result := 0;
 If Buf.Len <= 12 then Exit;
 If Not( ( (Buf.Data[2] and $80) = $80 ) and        // Answer
         ( (Buf.Data[5] and $01) = $01 ) and        // Question
         (  Buf.Data[7]          > $01 ) ) then
  Exit;
 //
 Cnt := Buf.Data[7];
 Inx := 0;
 For Lp := 12 to Buf.Len-1 do
  If Buf.Data[Lp] = 0 then
   begin
    Inx := Lp + 5 ; // Name Position
    Break;
   end;
 //
 For Lp := 1 to Cnt do
  begin
   Case Buf.Data[Inx] = $C0 of
    True : Begin // Compress
            Inc(Inx,2);
            Move(Buf.Data[Inx],DType,2); // Read Type
           End;
    False: For DType := Inx to Buf.Len-1 do
            If Buf.Data[DType] = 0 then
             begin
              Inx := DType+1;
              Move(Buf.Data[Inx],DType,2); // Read Type
              Break;
             end;
   End;
   DType := Swap(DType);
   //
   Case DType <> 1 of
    True : begin
            Inc(Inx,8); // Class,TLL,Data
            Move(Buf.Data[Inx],DLen,2); // Read Data Len
            DLen := Swap(DLen);
            Inc(Inx,2+DLen);            // Len + Data
           end;
    False: begin
            Inc(Inx,8); // Class,TLL,Data
            Move(Buf.Data[Inx],DLen,2);
            DLen := Swap(DLen);
            Case DLen of
             4 : begin
                  Inc(Inx,2);
                  Result := Inx;
                  Exit;
                 end;
             else Inc(Inx,2+DLen);      // Len + Data
            End;
           end;
   End;
  end;
end;

//
Procedure TCP_Send       (Var Pkt : TPkt);
 Var
  FCS : Word;
  Lp  : Word;
 begin
  //
  hwSnd (_cPPPFlag); // 7E
  hwSndCRC($FF                    ,fwStart,FCS); // Protocol
  hwSndCRC($03                    ,fwWork ,FCS); // Control
  hwSndCRC($00                    ,fwWork ,FCS); // TCP/IP
  hwSndCRC($21                    ,fwWork ,FCS); //
  // IP ------------------------------------------------------------
  hwSndCRC($45                    ,fwWork ,FCS); // VerLen
  hwSndCRC($00                    ,fwWork ,FCS); // TOS
  hwSndCRC(Hi(Pkt.TCPIP.Length   ),fwWork ,FCS); // Len ( IP + TCP + Data.Len )
  hwSndCRC(Lo(Pkt.TCPIP.Length   ),fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.ID       ),fwWork ,FCS); // ID
  hwSndCRC(Lo(Pkt.TCPIP.ID       ),fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.FlagIP   ),fwWork ,FCS); // FlagIP (3:13)
  hwSndCRC(Lo(Pkt.TCPIP.FlagIP   ),fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.TTL       ,fwWork ,FCS); // TTL
  hwSndCRC(   Pkt.TCPIP.Protocol  ,fwWork ,FCS); // Protocol
  hwSndCRC(Hi(Pkt.TCPIP.ChkSumIP ),fwWork ,FCS); // IP Check Sum
  hwSndCRC(Lo(Pkt.TCPIP.ChkSumIP ),fwWork ,FCS); // IP Check Sum
  hwSndCRC(   Pkt.TCPIP.SrcIP[0]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[1]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[2]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.SrcIP[3]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[0]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[1]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[2]  ,fwWork ,FCS); // SrcIP
  hwSndCRC(   Pkt.TCPIP.DstIP[3]  ,fwWork ,FCS); // SrcIP
  // TCP --------------------------------------------------------
  hwSndCRC(Hi(Pkt.TCPIP.SrcPort)  ,fwWork ,FCS); // Src Port
  hwSndCRC(Lo(Pkt.TCPIP.SrcPort)  ,fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.DstPort)  ,fwWork ,FCS); // Dst Port
  hwSndCRC(Lo(Pkt.TCPIP.DstPort)  ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.SegNum[0] ,fwWork ,FCS); // Seg Num.
  hwSndCRC(   Pkt.TCPIP.SegNum[1] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.SegNum[2] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.SegNum[3] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.Ack   [0] ,fwWork ,FCS); // Ack
  hwSndCRC(   Pkt.TCPIP.Ack   [1] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.Ack   [2] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.Ack   [3] ,fwWork ,FCS); //
  hwSndCRC(   Pkt.TCPIP.HLen      ,fwWork ,FCS); // Header Len (4*X)
  hwSndCRC(   Pkt.TCPIP.FlagTCP   ,fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.Win      ),fwWork ,FCS); // Win
  hwSndCRC(Lo(Pkt.TCPIP.Win      ),fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.ChkSumTCP),fwWork ,FCS); // TCP Check Sum
  hwSndCRC(Lo(Pkt.TCPIP.ChkSumTCP),fwWork ,FCS); //
  hwSndCRC(Hi(Pkt.TCPIP.Urgent   ),fwWork ,FCS); // Urgent
  hwSndCRC(Lo(Pkt.TCPIP.Urgent   ),fwWork ,FCS); //
  // TCP Data
  If Pkt.Data.Len > 0 then
   For Lp := 0 to Pkt.Data.Len-1 do
    hwSndCRC(Pkt.Data.Data[Lp],fwWork,FCS);
  //
  CRC_PPP(fwEnd ,0,FCS);
  // FCS
  hwSnd(Hi(FCS)  );
  hwSnd(Lo(FCS)  );
  hwSnd(_cPPPFlag);
  // Help...
  // Dbg('Snd:'+_gDStr);
  Help_TCP('Snd:',Pkt);
 end;

//
Procedure TCP_Open (Var Pkt : TPkt; Ip : TBuf4; Port : Word; Flag : Byte);
 Var
  Sav4 : TBuf4;
  Len  : Word;
 begin
  //
  Pkt.Env.TCPState   := tsOpen;
  // IP 설정 ------------------------------------------------------------------
  Pkt.TCPIP.VerLen   := $45;
  Pkt.TCPIP.TOS      := $00;
  Pkt.TCPIP.Length   := $2C;
  Pkt.TCPIP.ID       := $4040; // 3D31;
  Pkt.TCPIP.FlagIP   := $4000;
  Pkt.TCPIP.TTL      := $40;
  Pkt.TCPIP.Protocol := _cIP_TCP;
  Pkt.TCPIP.SrcIp    := Pkt.Env.IPClient; // B2Buf4($0A,$17,$06,$04); //
  Pkt.TCPIP.DstIP    := IP;               // B2Buf4($76,$21,$72,$E8);
  // TCP 설정 -----------------------------------------------------------------
  Pkt.TCPIP.SrcPort  := $764A;
  Pkt.TCPIP.DstPort  := Port;
  Case (Flag and _cSyn) = _cSyn of
   True : begin
           Pkt.TCPIP.SegNum   := B2Buf4($00,$42,$24,$5C);
           Pkt.TCPIP.Ack      := B2Buf4($00,$00,$00,$00);
          end;
   False: begin
           Sav4               := Pkt.TCPIP.SegNum;     // Ack,Seq Swap
           Pkt.TCPIP.SegNum   := Pkt.TCPIP.Ack;
           Pkt.TCPIP.Ack      := Sav4;
           //
           Len := Pkt.TCPIP.HLen shr 4;
            Case Len > 5 of
             True  : Len := (Len-5)*4;
             False : Len := 0;
            End;
           Pkt.TCPIP.Ack := DWToB4(B4ToDW(Pkt.TCPIP.ACk)+
                                  Pkt.Data.Len - Len);
          

          end;
  End;
  Pkt.TCPIP.FlagTCP  := Flag;
  Pkt.TCPIP.Win      := $01FF; // Buffer 사이즈
  Pkt.TCPIP.Urgent   := $0000;
  //         ------------------------------------------------------------------
  Case (Flag and _cSyn) = _cSyn of
   True : begin
           Pkt.TCPIP.HLen     := $60;
           Pkt.Data.Len       := 4;
           Pkt.Data.Data[0]   := $02;
           Pkt.Data.Data[1]   := $04;
           Pkt.Data.Data[2]   := $00;
           Pkt.Data.Data[3]   := $D2;
          end;
   False: begin
           Pkt.TCPIP.HLen     := $50;
           Pkt.Data.Len       := 0;
          end;
  End;
  // Check Sum ----------------------------------------------------------------
  Pkt.TCPIP.ChkSumIP  := CRC_TCPUDP(Pkt,ctIP );
  Pkt.TCPIP.ChkSumTCP := CRC_TCPUDP(Pkt,ctTCP);
  //
  TCP_Send(Pkt);
 end;

//
Procedure TCP_Reply(Var Pkt : TPkt);
 Var
  Lp    : Byte;
  Sav4  : TBuf4;
  Sav2  : Word;
  Len   : Integer;
 begin
  _gDStr := '';
  //
  If Pkt.TCPIP.Protocol = _cIP_UDP then
   begin
    Dbg('### Rcv UDP , Pass... ###');
    Exit;
   end;
  // ---------------------------------------------------------------
  Case Pkt.Env.TCPState of
   tsOpen : Begin
             // IP 관련 설정 ---------------------------------------------------
             Pkt.TCPIP.ID       := $3D32;
             Pkt.TCPIP.FlagIP   := $4000;
             Pkt.TCPIP.TTL      := $40;
             Pkt.TCPIP.Protocol := _cIP_TCP;
             Sav4               := Pkt.TCPIP.SrcIP;   // IP Swap
             Pkt.TCPIP.SrcIp    := Pkt.TCPIP.DstIP;
             Pkt.TCPIP.DstIP    := Sav4;
             // TCP 관련 설정 --------------------------------------------------
             Sav2               := Pkt.TCPIP.SrcPort; // Port Swap
             Pkt.TCPIP.SrcPort  := Pkt.TCPIP.DstPort;
             Pkt.TCPIP.DstPort  := Sav2;
             Sav4               := Pkt.TCPIP.SegNum;     // Ack,Seq Swap
             Pkt.TCPIP.SegNum   := Pkt.TCPIP.Ack;
             Pkt.TCPIP.Ack      := Sav4;

             Pkt.TCPIP.HLen     := $50;
             Pkt.TCPIP.Win      := $01FF; // Buffer 사이즈
             //
             Pkt.Data.Len       := 0;
             Pkt.TCPIP.Length   := 20 + 20 + Pkt.Data.Len;
             //
             Case (Pkt.TCPIP.FlagTCP and _cSyn) = _cSyn of
              True : Begin
                      Pkt.TCPIP.Ack := DWToB4(B4ToDW(Pkt.TCPIP.Ack)+1);
                      Pkt.Env.TCPState  := tsWork;
                      Pkt.TcpIp.FlagTCP := _cAck;
                      Dbg('### TCP/IP : Open / ');
                     End;
              False: Begin
                      Dbg('### TCP/IP : Open / Exit #### 문제 발생 #### ');
                      Pkt.TCPIP.Ack := DWToB4(B4ToDW(Pkt.TCPIP.Ack)+1);
                      Pkt.Env.TCPState  := tsWork;
                      Pkt.TcpIp.FlagTCP := _cAck;
                     End;
             End;
            End;
   tsWork : Begin
             Exit;
             Len := Pkt.TCPIP.HLen shr 4;
             Case Len > 5 of
              True  : Len := (Len-5)*4;
              False : Len := 0;
             End;
             Pkt.TCPIP.Ack := DWToB4(B4ToDW(Pkt.TCPIP.ACk)+
                                     Pkt.Data.Len - Len);
             Pkt.TcpIp.FlagTCP := _cAck;
             Dbg('### TCP/IP : Work ' +
                 IntToStr(Pkt.Data.Len));
             //
             Pkt.Data.Len       := 0;
             Pkt.TCPIP.Length   := 20 + 20 + Pkt.Data.Len;
            End;
  End;
  //
  Pkt.TCPIP.ChkSumIP  := CRC_TCPUDP(Pkt,ctIP );
  Pkt.TCPIP.ChkSumTCP := CRC_TCPUDP(Pkt,ctTCP);
  //
  TCP_Send(Pkt);
 end;

//
Procedure ppp_Init   (Var Pkt    : TPkt);
 begin
  Pkt.iEscaped    := False;
  Pkt.iInx        := 0;
  //FillChar(Pkt.Env,SizeOf(Pkt.Env),#0);
  Pkt.State       := psStart;
  Pkt.Frame.State := sfCode;
  Pkt.TcpIp.State := stVerLen;
  //
  Pkt.Type_       := ptLCP;
 end;

//
Procedure Frame_Receive (Var Pkt : TPkt;InData : Byte);
 begin
  //
  Case Pkt.Frame.State of
   sfCode : Begin
             Pkt.Frame.Code  := InData;
             Pkt.Frame.State := sfID;
            End;
   sfID   : Begin
             Pkt.Frame.ID    := InData;
             Pkt.Frame.State := sfLen;
             Pkt.iInx        := 0;
            End;
   sfLen  : Begin
             Case Pkt.iInx of
              0 : Pkt.Frame.Len := InData;
              1 : Pkt.Frame.Len := (Pkt.Frame.Len shl 8) or InData;
             End;
             Inc(Pkt.iInx);
             If Pkt.iInx >= 2 then
              begin
               Pkt.Frame.State := sfData;
               Pkt.Data.Len    := 0;
              end;
            End;
   sfData : Begin
             Pkt.Data.Data[Pkt.Data.Len] := InData;
             Inc(Pkt.Data.Len);
             // 4 = Code[1] + ID[1] + Len[2]
             If Pkt.Data.Len >= (Pkt.Frame.Len - 4)  then
              begin
               Pkt.State := psFCS;
               Pkt.iInx  := 0;
              end;
            End;
  End;
 end;

Function Hdr_Len(Protocol : Byte ) : Byte;
 begin
  //
  Result := 40;
  If Protocol = _cIP_UDP then
   Result := 28;
 end;

//
Procedure TCP_Receive(Var Pkt : TPkt;InData : Byte);
 Var
  ChkSum : Word;
 begin
  //
  Case Pkt.TcpIp.State of
                // IP -----------------------------------------------------
   stVerLen   : Begin
                 Pkt.TcpIp.VerLen := InData;
                 Pkt.TcpIp.State  := stTOS;
                End;
   stTOS      : Begin
                 Pkt.TcpIp.TOS    := InData;
                 Pkt.TcpIp.State  := stLen;
                 Pkt.iInx      := 0;
                End;
   stLen      : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.Length := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.Length := (Pkt.TcpIp.Length shl 8) or InData;
                      Pkt.TcpIp.State  := stID;
                      Pkt.iInx      := 0;
                     End;
                End;
   stID       : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.ID      := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.ID     := (Pkt.TcpIp.ID shl 8) or InData;
                      Pkt.TcpIp.State  := stFlagIP;
                      Pkt.iInx      := 0;
                     End;
                End;
   stFlagIP   : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.FlagIP := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.FlagIP := (Pkt.TcpIp.FlagIP shl 8) or InData;
                      Pkt.TcpIp.State  := stTTL;
                      Pkt.iInx      := 0;
                     End;
                End;
   stTTL      : Begin
                 Pkt.TcpIp.TTL      := InData;
                 Pkt.TcpIp.State    := stProtocol;
                End;
   stProtocol : Begin
                 Pkt.TcpIp.Protocol := InData;
                 Pkt.TcpIp.State    := stChkSumIP;
                 Pkt.iInx        := 0;
                End;
   stChkSumIP : Case Pkt.iInx of
                 0 : begin
                      Pkt.TcpIp.ChkSumIP := InData;
                      Inc(Pkt.iInx);
                     end;
                 1 : begin
                      Pkt.TcpIp.ChkSumIP := (Pkt.TcpIp.ChkSumIP shl 8) or InData;
                      Pkt.TcpIp.State    := stSrcIP;
                      Pkt.iInx           := 0;
                     end;
                End;
   stSrcIP    : Begin
                 Pkt.TcpIp.SrcIP[Pkt.iInx] := InData;
                 Inc(Pkt.iInx);
                 If Pkt.iInx >= 4 then
                  begin
                   Pkt.TcpIp.State := stDstIP;
                   Pkt.iInx     := 0;
                  end;
                End;
   stDstIP    : Begin
                 Pkt.TcpIp.DstIP[Pkt.iInx] := InData;
                 Inc(Pkt.iInx);
                 If Pkt.iInx >= 4 then
                  begin
                   Pkt.TcpIp.State := stSrcPort;
                   Pkt.iInx     := 0;
                  end;
                End;
                // TCP ---------------------------------------------------------
   stSrcPort  : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.SrcPort := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.SrcPort := (Pkt.TcpIp.SrcPort shl 8) or InData;
                      Pkt.TcpIp.State   := stDstPort;
                      Pkt.iInx          := 0;
                     End;
                End;
   stDstPort  : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.DstPort := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.DstPort := (Pkt.TcpIp.DstPort shl 8) or InData;
                      Pkt.iInx          := 0;
                      //
                      Case Pkt.TcpIp.Protocol of
                       _cIP_TCP : Pkt.TcpIp.State := stSeqNum;
                       _cIP_UDP : Pkt.TcpIp.State := stWin;
                      End;
                     End;
                End;
   stSeqNum   : Begin
                 Pkt.TcpIp.SegNum[Pkt.iInx] := InData;
                 Inc(Pkt.iInx);
                 If Pkt.iInx >= 4 then
                  begin
                   Pkt.TcpIp.State := stAck;
                   Pkt.iInx     := 0;
                  end;
                End;
   stAck      : Begin
                 Pkt.TcpIp.Ack[Pkt.iInx] := InData;
                 Inc(Pkt.iInx);
                 If Pkt.iInx >= 4 then
                  begin
                   Pkt.TcpIp.State := stHLen;
                   Pkt.iInx     := 0;
                  end;
                End;
   stHLen     : Begin
                 Pkt.TcpIp.HLen    := InData;
                 Pkt.TcpIp.State   := stFlagTCP;
                End;
   stFlagTCP  : Begin
                 Pkt.TcpIp.FlagTCP := InData;
                 Pkt.TcpIp.State   := stWin;
                 Pkt.iInx          := 0;
                End;
   stWin      : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.Win := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.Win := (Pkt.TcpIp.Win shl 8) or InData;
                      Pkt.TcpIp.State   := stChkSumTCP;
                      Pkt.iInx          := 0;
                     End;
                End;
   stChkSumTCP: Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.ChkSumTCP := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.ChkSumTCP := (Pkt.TcpIp.ChkSumTCP shl 8) or InData;
                      Pkt.iInx          := 0;
                      Pkt.Data.Len      := 0;
                      //
                      Case Pkt.TcpIp.Protocol of
                       _cIP_TCP : Pkt.TcpIp.State := stUrgent;
                       _cIP_UDP : Pkt.TcpIp.State := stData;
                      End;
                     End;
                End;
   stUrgent   : Case Pkt.iInx of
                 0 : Begin
                      Pkt.TcpIp.Urgent  := InData;
                      Inc(Pkt.iInx);
                     End;
                 1 : Begin
                      Pkt.TcpIp.Urgent  := (Pkt.TcpIp.Urgent shl 8) or InData;
                      Pkt.TcpIp.State   := stData;
                      Pkt.iInx          := 0;
                      Pkt.Data.Len      := 0;
                      If Pkt.TcpIp.Length = Hdr_Len(Pkt.TcpIp.Protocol) then
                       begin
                        Pkt.State := psFCS;
                        Pkt.iInx  := 0;
                       end;
                     End;
                End;
   stData     : Begin
                 Pkt.Data.Data[Pkt.Data.Len] := InData;
                 Inc(Pkt.Data.Len);
                 If Pkt.Data.Len >= (Pkt.TCPIP.Length - Hdr_Len(Pkt.TcpIp.Protocol)) then
                  begin
                   Pkt.State := psFCS;
                   Pkt.iInx  := 0;
                   //
                   ChkSum := CRC_TcpUDP(Pkt,ctIP);
                   If Pkt.TCPIP.ChkSumIP <> ChkSum then
                    Dbg('#### Err IP CRC : Pkt:' + W2H(Pkt.TCPIP.ChkSumIP) +
                                        '/Calc:' + W2H(ChkSum));
                   //
                   Case Pkt.TCPIP.Protocol of
                    _cIP_TCP : ChkSum := CRC_TcpUDP(Pkt,ctTCP);
                    _cIP_UDP : ChkSum := CRC_TcpUDP(Pkt,ctUDP);
                   End;
                   If Pkt.TCPIP.ChkSumTCP <> ChkSum then
                    Dbg('#### Err TCP/UDP CRC : Pkt:' + W2H(Pkt.TCPIP.ChkSumTCP) +
                                             '/Calc:' + W2H(ChkSum));
                  end;
                End;
  End;
 end;

//
Function  ppp_Receive(Var Pkt : TPkt; InData : Byte) : Boolean;
 begin
  //
  Result := False;
  // Process Escape Sequence --------------------------------------------------
  If InData = _cEscape then begin
                             Pkt.iEscaped := True;
                             Exit;
                            end;
  If Pkt.iEscaped      then begin
                             InData := InData xor $20;
                             Pkt.iEscaped := False;
                            end;
  // State --------------------------------------------------------------------
  Case Pkt.State of
   psStart   : If InData = _cPPPFlag then
                begin
                 ppp_Init(_gPkt);
                 Pkt.State := psAddr;
                end;
   psAddr    : // $7E $FF $03 $C0 $21 : LCP
               // $7E         $80 $21 : IPCP
               // $7E             $21 : TCP,IP
               // $7E $FF $03 $00 $2F : Uncomp TCP/IP
               // $7E             $2F : Uncomp TCP/IP
               // $7E $FF $03 $00 $2D : Comp   TCP/IP
               // $7E             $2D : Comp   TCP/IP
               Case InData of
                $21,  // UnCompressed
                $2F,  // UnCompressed
                $2D : // Compressed
                      begin
                       Pkt.Type_ := ptTCP;
                       Pkt.State := psData;
                       CRC_PPP(fwStart,InData,Pkt.iFCS);
                      end;
                $7E : ;
                $80 : begin
                       Pkt.iInx           := 0;
                       Pkt.iBuf[Pkt.iInx] := InData;
                       Inc(Pkt.iInx);
                       Pkt.State          := psProtocol;
                       CRC_PPP(fwStart,InData,Pkt.iFCS);
                      end;
                $FF : begin
                       Pkt.Type_ := ptNone;
                       Pkt.State := psControl;
                       CRC_PPP(fwStart,InData,Pkt.iFCS);
                      end
                else  Pkt.State := psStart;
               End;
   psControl : Begin
                Pkt.State := psProtocol;
                Pkt.iInx  := 0;
                CRC_PPP(fwWork,InData,Pkt.iFCS);
               End;
   psProtocol: Begin
                Pkt.iBuf[Pkt.iInx] := InData;
                //
                CRC_PPP(fwWork ,InData,Pkt.iFCS);
                //
                Inc(Pkt.iInx);
                If Pkt.iInx >= 2 then
                 Begin
                  //
                  Case (Pkt.iBuf[0] shl 8) or
                       (Pkt.iBuf[1]      ) of
                   _cPktLCP  : Pkt.Type_ := ptLCP;   // LCP
                   _cPktCHAP : Pkt.Type_ := ptChap;  // Chap
                   _cPktIPCP : Pkt.Type_ := ptIPCP;  // IPCP
                   $0021,      // UnComp
                   $002F,      // UnComp
                   _cPktCTCP : // Comp
                               Pkt.Type_ := ptTCP;   // TCP,UDP
                   else        Pkt.State := psStart;
                  end;
                  //
                  If Pkt.State <> psStart then
                   Pkt.State := psData;
                 End;
               End;
   psData    : // 타입별로 Parsing
               Begin
                Case Pkt.Type_ of
                 ptLCP,
                 ptChap,
                 ptIPCP : Frame_Receive(Pkt,InData);
                 ptTCP  : TCP_Receive  (Pkt,InData);
                 else     Pkt.State := psStart;
                End;
                CRC_PPP(fwWork,InData,Pkt.iFCS);
               End;
   psFCS     : Begin
                Pkt.iBuf[Pkt.iInx] := InData;
                Inc(Pkt.iInx);
                If Pkt.iInx >= 2 then
                 begin
                  CRC_PPP(fwEnd,InData,Pkt.iFCS);
                  Pkt.rFCS := (Pkt.iBuf[0] shl 8) or
                              (Pkt.iBuf[1]      );
                  Pkt.State := psStop;
                  If Pkt.iFCS <> Pkt.rFCS then
                   Dbg('#### Err FCS : Pkt:'+W2H(Pkt.iFCS)+
                                    '/Calc:'+W2H(Pkt.rFCS));
                 end;
               End;
   psStop    : //
               Begin
                Pkt.State       := psStart;
                If InData = _cPPPFlag then
                 Result := True;
               End;
  End;
 end;

Function ppp_process    (Var Pkt : TPkt; InData : Byte) : Boolean;
 Var
  Lp : Integer;
 begin
  Result := PPP_Receive(Pkt,InData);
  If Result then
   Begin
    // 받은 패킷 보여주고...
    Help_Pkt('Rcv:',_gPkt);
    //
    Case _gPkt.Type_ of
     ptLCP  : Case _gPkt.Frame.Code of
               _cConf_Req : begin
                             Case Pkt.iStep of
                              ns0_Init : begin
                                          Dbg('Snd  LCP Request / 신규 채널 ##');
                                          hwSndBuf(@_cLCP_Req,SizeOf(_cLCP_Req));
                                         end;
                             End;
                             // 처음 패킷 응답
                             Reply_LCP(Pkt.iStep,_gPkt);
                             Pkt.iStep := ns1_LCP_Req;
                            end;
               _cConf_Ack : Begin
                             Dbg('####### Chap Send XXXXXXXXX');
                             hwSndBuf(@_cChap_Res,SizeOf(_cChap_Res));
                             //For Lp := 0 to 10 do
                             // begin
                             //  Sleep(20);
                             //  Application.ProcessMessages;
                             // end;
                             //Dbg('####### LCP 종료/ IPCP Request 보냄 #1  ###');
                             //hwSndBuf(@_cIPCP_Req,SizeOf(_cIPCP_Req));
                            End;
              End;
     ptChap : Case _gPkt.Frame.Code of
               _cConf_Req : Dbg('Rcv Chap Req ');
               _cConf_Ack : Dbg('Rcv Chap Ack ');
              End;
     ptIPCP : Reply_IPCP(Pkt.iStep,_gPkt);
     ptTCP  : TCP_Reply (_gPkt);
    End;
   End;
 end;

Procedure Send_IPCP_Req;
 begin
  Dbg('####### 수동  IPCP Request 보냄 #1  ###');
  hwSndBuf(      @_cIPCP_Req2,
           SizeOf(_cIPCP_Req2));
 end;

Initialization
  FillChar(_gPkt.Env,SizeOf(_gPkt.Env),#0);
  ppp_Init(_gPkt);
end.
 

댓글 없음: