Bootstrap

IP报文解析(TCP、UDP、 ICMP)及代码分享(C++)

一、OSI模型与TCP/IP协议栈

1.1 OSI 7层模型:

应用层:
功能:用户接口,文件传输、电子邮件、虚拟终端、文件服务
设备:网关
协议:HTTP、TFTP、SMTP、FTP、SNMP、DNS、Telnet

表示层:
功能:数据的表示,压缩和加密
设备:网关
协议:无

会话层:
功能:会话的建立和结束
设备:网关
协议:无

传输层:
功能:提供端对端的接口
设备:网关
协议:TCP UDP

网络层:
功能:为数据报选择路由,寻址
设备:路由器
协议:IP、ICMP、IGMP、RIP

数据链路层:
功能:传输有地址的帧与错误校验功能
设备:交换机、网桥、网卡
协议:PPP、ARP、MTU、RARP、SLIP、CSLIP

物理层:
功能:传输比特流,以二进制数据形式在物理媒体上传输数据
设备:集线器、中继器
协议:IEEE802、IEEE802.2、ISO2110

在这里插入图片描述

1.2 数据封装过程:

![在这里插入图片描述](https://img-blog.csdnimg.cn/03677d210ad94f449f4495d5c3690ee3.png
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
图片来源:https://blog.csdn.net/shimazhuge/article/details/5382725?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-5-5382725-blog-78396507.235v29pc_relevant_default_base3&spm=1001.2101.3001.4242.4&utm_relevant_index=8

1.3 TCP/IP 4层模型:

应用层:
功能:用户接口,文件传输、电子邮件、虚拟终端、文件服务
设备:网关
协议:HTTP、TFTP、SMTP、FTP、SNMP、DNS、Telnet

传输层:
功能:提供端对端的接口
设备:网关
协议:TCP UDP

网络层:
功能:为数据报选择路由,寻址
设备:路由器
协议:IP、ICMP、IGMP、RIP

数据链路层:
功能:传输有地址的帧与错误校验功能
设备:交换机、网桥、网卡
协议:PPP、ARP、MTU、RARP、SLIP、CSLIP

二、报文解析

2.1 IP报文

2.1.1 IP报文格式

在这里插入图片描述
在这里插入图片描述

2.1.2 IP报文解析及校验和计算:

十六进制加法计算器:
https://www.9321.cn/digital-computation/hex-addition-calculator.php

crc校验:http://www.ip33.com/crc.html

在这里插入图片描述

在这里插入图片描述

2.2 UDP报文

2.1.1 UDP报文格式:

在这里插入图片描述

2.1.2 UDP报文解析及校验和计算:

UDP的校验和需要计算UDP首部加数据荷载部分,但也需要加上UDP伪首部。这个伪首部指,源地址、目的地址、UDP数据长度、协议类型(0x11),协议类型就一个字节,但需要补一个字节的0x0,构成12个字节。伪首部+UDP首部+数据一起计算校验和。

在这里插入图片描述

2.3 TCP报文

2.3.1 TCP报文格式

在这里插入图片描述
1> 16位源端口号,2个字节。客户端通常使用系统自动选择的临时端口号。
2> 16位目的端口号,2个字节。服务器的端口号。
3> 32位序号,4个字节。A与B连接后发送第一个报文段,序号值被系统分配随机一个值S,后续报文段的序号值为S + 偏移值(该报文段第一个字节在整个字节流中第几个字节,例如,某个TCP报文段传送的数据是字节流中的第1025~2048字节,那么该报文段的序号值就是ISN+1025)
4> 32位确认号,4个字节。用作对另一方发送来的TCP报文段的响应。其值是收到的TCP报文段的序号值加1
5> 4位头部长度,表示有多少个32位字(4字节),4位最大值是15,所以头部最长60字节。
6> 6位保留。
7> 6位,ACK: 表示确认号是否有效。 PSH: 提示接收端应用程序应该立即从TCP接收缓冲区中读走数据。RST: 表示要求对方重新建立连接。 SYN: 表示请求建立一个连接。FIN:表示通知对方本端要关闭连接了。URG(紧急位):设置为1时,首部中的紧急指针有效;为0时,紧急指针没有意义。
8> 16位窗口大小,2个字节。告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。最大位65535字节。窗口大小为0时候,说明数据被截断
9> 16位校验和,2个字节。由发送端填充,接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程中是否损坏。注意,这个校验不仅包括TCP头部,也包括数据部分。
10> 16位紧急指针,2个字节。
11> TCP头部选项,最多40字节。因为TCP头部最长60字节(其中还包含前面讨论的20字节的固定部分)

在这里插入图片描述

2.3.2 TCP三次握手和四次挥手过程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.4 ICMP报文

2.4.1 ICMP报文格式

ping使用的是 ICMP 的请求回显/回显应答类型的报文,它的内容包括标识符、序列号以及回显数据3部分,报文大小默认为 64 字节(header的8字节+body的56字节)。

在这里插入图片描述

2.4.2 ICMP报文解析及校验和计算

ICMP校验和:
生成:先将校验和置为0,然后将ICMP报文的header+body按16bit分组求和。如果结果溢出,则将高16位和低16位求和,直到高16位为0。最后求反就是检验和的值。

在这里插入图片描述

三、相关代码分享:

BOOL CSerialDlg::TestPing()
{
    string sDesIP = "202.108.22.5";
    int nDestIP[4];
    int k,l,mm;
    char DestIP0[2];
    char DestIP1[2];
    char DestIP2[2];
    char DestIP3[2];
	CString strText;
    CString strVersion;
    CString strPath = _T("FM100M001.ini");
    BYTE temp[256];
    memset(temp, 0x00, 256);
    m_PPPPacket.bRecPing = FALSE;

    AddText(_T("Ping测试开始"));
    if(!GetInitFileStrings(strPath, _T("PPP"), _T("Ping"), sDesIP))
    {
        ffprint("获取配置文件Ping的IP失败");
    }
    ffprint("ping sDesIP:%s", sDesIP.c_str());
            
    //Dest IP
    vector<string> vec = CStringUtils::Split(sDesIP, _T("."));
    if(vec.size() >= 4)
    {
        for(int i = 0; i < 4; i++)
        {
            nDestIP[i] = atoi(vec[i].c_str());
        }
        DectoHex(nDestIP[0], DestIP0, 1);
        DectoHex(nDestIP[1], DestIP1, 1);
        DectoHex(nDestIP[2], DestIP2, 1);
        DectoHex(nDestIP[3], DestIP3, 1);
        
        temp[0] = DestIP0[0];	
        temp[1] = DestIP1[0];
        temp[2] = DestIP2[0];
        temp[3] = DestIP3[0];
    }

    //数据长度
	CString m_senddata = _T("AT");
	temp[4] = strlen(m_senddata)>>8;
	temp[5] = strlen(m_senddata)&0x00ff;

	//数据
	CString strData = Ascii2Hex(m_senddata);
    char DataHex[256] = {0};
    String2Hex(strData, DataHex);
    
	mm = strlen(m_senddata)+6;
	l = 0;
	for (k=6; k<mm; k++)
	{
		temp[k] = m_senddata[l++];
	}

    int nPingNum = 10;
    string sPingNum;
    if(GetInitFileStrings(strPath, _T("PPP"), _T("PingNum"), sPingNum))
    {
        nPingNum = atoi(sPingNum.c_str());
        ffprint("GetInitFileStrings nPingNum:%d", nPingNum);
    }

    for(int i = 0 ; i < nPingNum; i++)
    {
        ::WaitForSingleObject(m_hStopEvent, 100);
    
        m_PPPPacket.IP_header.protocol = IP_ICMP;
    	m_PPPPacket.CreateIpPkt(IP_ICMP, ICMP_PING, temp);
    	if(m_PPPPacket.TxLen > 0)
        {
            strText.Format(_T("Ping %s 中..."), sDesIP.c_str());
            AddText(strText);
          
            int iRet = SendHex(m_PPPPacket.PacketTx, STATE_NCP_PING, m_PPPPacket.TxLen, 10000);
            if (iRet != -1)
            {
                if(m_PPPPacket.bRecPing)
                {
                    strText.Format(_T("Ping %s通过,网络正常."), sDesIP.c_str());
                    AddText(strText, 1);
                    PushResult(strText, strText.GetLength(), 1);
                    return TRUE;
                }
            }
        } 
    }

    strText.Format(_T("Ping %s不通过,网络异常."), sDesIP.c_str());
    AddText(strText, 2, true);
	PushResult(strText, strText.GetLength(), 2);

	return FALSE;
}

BOOL CSerialDlg::TestUdp()
{
    CString strText;

    //Dest IP
    CString m_ipOut0;
	CString m_ipOut1;
	CString m_ipOut2;
	CString m_ipOut3;
    BYTE temp[256];
    int k,l,j;
	memset(temp,0x00,256);

    m_ipOut0 = _T("120");			
	m_ipOut1 = _T("42");
	m_ipOut2 = _T("46");
	m_ipOut3 = _T("98");

    int nDestIP0 = atoi(m_ipOut0);
    int nDestIP1 = atoi(m_ipOut1);
    int nDestIP2 = atoi(m_ipOut2);
    int nDestIP3 = atoi(m_ipOut3);

    char DestIP0[2];
    char DestIP1[2];
    char DestIP2[2];
    char DestIP3[2];
    DectoHex(nDestIP0, DestIP0, 1);
    DectoHex(nDestIP1, DestIP1, 1);
    DectoHex(nDestIP2, DestIP2, 1);
    DectoHex(nDestIP3, DestIP3, 1);

    temp[0] = DestIP0[0];	
	temp[1] = DestIP1[0];
	temp[2] = DestIP2[0];
	temp[3] = DestIP3[0];
    
    
    //Src Port and Dest Port
    CString m_srcport = _T("4321");
    CString m_desport = _T("7100");
    temp[4] = atoi(m_srcport)>>8;			//source port msb
	temp[5] = atoi(m_srcport)&0x00ff;		//source port lsb
	temp[6] = atoi(m_desport)>>8;			//destination port msb
	temp[7] = atoi(m_desport)&0x00ff;	    //destination port lsb


    //length
    CString m_senddata = _T("UDP Test");
	j = strlen(m_senddata) + 8;
	temp[8] = j>>8;	                        //the length of send udp data's msb
	temp[9] = j&0x00ff;	                    //the length of send udp data's lsb
	temp[10] = 0;
	temp[11] = 0;


    //Data
    CString strData = Ascii2Hex(m_senddata);
    char DataHex[256] = {0};
    String2Hex(strData, DataHex);
    
    l=0;
	for (k=12; k<j+4; k++)
	{
		temp[k] = DataHex[l++];
	}
    
	m_PPPPacket.IP_header.protocol = IP_UDP;
	m_PPPPacket.CreateIpPkt(IP_UDP, NULL, temp);
	if(m_PPPPacket.TxLen > 0)
    {
        std::string hexStr2 = HexToString(m_PPPPacket.PacketTx, m_PPPPacket.TxLen);
                                    
        strText.Format(_T("发送:%s"), hexStr2.c_str());
        AddText(strText);
        
        if(m_serial.SendDataFromSerial(m_PPPPacket.PacketTx, m_PPPPacket.TxLen))
        {
            CString sMsg = _T("UDP测试数据已发送, 确认UDP报文是否被接收.\r\n(点击确认按钮继续下面测试,点击取消按钮结束测试.)");
            BOOL bResult = FALSE;
            int msgboxID = MessageBox(sMsg, _T("UDP发送测试"), MB_OKCANCEL | MB_ICONQUESTION);
            switch(msgboxID)
            {
                case IDCANCEL:
                    bResult = FALSE;
                break;
                case IDOK:
                    bResult = TRUE;
                break;
            }

            if(bResult)
            {
                strText = _T("UDP发送测试通过");
		        AddText(strText, 1);
		        PushResult(strText, strText.GetLength(), 1);
                return TRUE;
            }
            else
            {
                strText = _T("UDP发送测试失败");
                AddText(strText, 2, true);
                PushResult(strText, strText.GetLength(), 2);
                return FALSE;
            } 
        }
    }

    strText = _T("UDP数据发送失败");
    AddText(strText, 2, true);
	PushResult(strText, strText.GetLength(), 2);
   
    return FALSE;
}

PPP_Packet.h

/* ***************************************************************
 * Filename:            Packet.h
 *  @Description:
 *         Packet Creat Check
 *  @Author: ybLin
 * ***************************************************************/
#ifndef __PPP_PACKET_H__
#define __PPP_PACKET_H__

#include <windows.h>
#include "crc.h"
#include <afxext.h> 

#define PPP_FRAME_FLAG		0x7E 	 	//标识字符 
#define PPP_FRAME_ESC		0x7D 	 	//转义字符 
#define PPP_FRAME_ENC		0x20 	 	//编码字符 
#define MAX_RECV_PKT_SIZE	256
#define MIN_RECV_PKT_SIZE 	12
#define PROTOCOL_LCP		0xC021      //LCP协议
#define PROTOCOL_IPCP	    0x8021      //NCP协议:IPCP
#define PROTOCOL_IP		    0x0021      //IP协议
#define PROTOCOL_PAP		0xC023      //PAP认证

extern BYTE g_LcpFirstReq[256];
extern BYTE g_TermReq[256];
extern BYTE g_NcpFirstReq[256];
extern std::string HexToString(const BYTE *pBuffer, size_t iBytes);
extern void DectoHex(int dec, char *hex, int length);
extern unsigned long HextoDec(const unsigned char *hex, int length); 
extern CString Ascii2Hex(CString strASCII);
extern char Char2Hex(char ch);
extern int String2Hex(CString str, char* SendOut);

//执行过程
typedef enum PPP_STATE
{
    PPP_STATE_INIT = 0,
    PPP_STATE_LCP_PERIOD,
    PPP_STATE_LCP_PASS,
    /*
    PPP_STATE_PAP_START,
    PPP_STATE_PAP_PASS,*/
    PPP_STATE_NCP_PERIOD,
    PPP_STATE_NCP_NAK,
    PPP_STATE_PPP_PASS,
    PPP_STATE_IP_START,
    PPP_STATE_TERM_LINK
}PPP_STATE_E;

//编码值
typedef enum PPP_CODE
{
    PPP_CODE_REQ = 1,   				//配置请求
    PPP_CODE_ACK,       				//接受配置
    PPP_CODE_NAK,       				//配置请求接受,其他拒绝
    PPP_CODE_REJ,       				//配置请求不认识,或不被接受
    PPP_CODE_TERM_LINK, 				//终止链接
    PPP_CODE_TERM_ACK,  				//终止确认
    PPP_CODE_CODE_REJ,
    PPP_CODE_PROTOCAL_REJ,
    PPP_CODE_ECHO_REQ,
    PPP_CODE_ECHO_REP,
    PPP_CODE_DISCARD_REQ,
    PPP_CODE_IDENTIFICATION,
    PPP_CODE_TIME_REM
}PPP_CODE_E;

//报文解析返回
enum PPP_OPERATE_STATE
{
	PPP_OPERATE_ERROR = -1,
	PPP_OPERATE_NORMAL,
	PPP_OPERATE_NEED_SEND
};

//选项值
typedef struct PPP_OPTION
{
    BYTE bType;
	BYTE bLength;
	BYTE bData[64];
	BOOL bReject;

}PPP_OPTION_T;

//IP头
typedef struct IP_HEADER
{
    BYTE Version;	//4bits
	BYTE IHL;		//4bits
	BYTE Service;
	unsigned short TotalLength;
	unsigned short Identification = 0x0000;
	BYTE FLAG;
	unsigned short FlagFrag;	
	BYTE TTL;
	BYTE protocol;
	BYTE HeaderSum;	
	BYTE SrcIP[4];	
	BYTE DesIP[4];
}IP_HEADER_T;

//ICMP头
typedef struct ICMP_HEADER
{
    BYTE type;
	BYTE code;
	unsigned short sum;
	unsigned short identifier;
	unsigned short sequence = 0x0000;
}ICMP_HEADER_T;

//IP
#define		IP_ICMP			0x01
#define		IP_TCP			0x06
#define		IP_UDP			0x11

//ICMP
#define		ICMP_PING		0x08
#define		ICMP_PINGREPLY	0x00



class CPacket  
{
public:
    CPacket();
    virtual ~CPacket();

    //初始化
    void InitPacket();
    
    //解析报文
    int Parsepkt(BYTE *pPkt, int nLen); 

    //检测配置选项
    void CheckOption(WORD wProType, BYTE CodeVal, BYTE* pOption);

    //创建PPP报文
    void CreatePPPPkt(WORD wProType, BYTE CodeVal, BYTE* pPkt, bool bActive = FALSE);
    
    //创建IP报文
    void CreateIpPkt(BYTE protocal, BYTE type, BYTE *temp);
 
private:
    //字符编码
    int CharacterEncode(int iLen);

    //转义编码
    int TransferEncode(unsigned char *pInPkt, int nInLen, unsigned char *pOutPkt);
    //转义解码
    int TransferDecode(unsigned char *pInPkt, int nInLen, unsigned char *pOutPkt);

    //日志输出
    void WriteLog(CString temp);


public:
	WORD wProtocolType; 		//协议类型 2个字节 
	BYTE PktID;		    		//包 ID 
	BOOL bReject;				//判断包是 ACK     或者 REJ
	BYTE CurState;              //PPP状态
    BYTE SrcIP[4];              //源动态IP
    int nOptionNum;             //配置选项数量
    BYTE PacketTx[256];         //发送包
	BYTE PacketTx1[256];	    //发送包
	BYTE PacketRx[256];         //接受包
	int TxLen;                  //发送包长度
	int RxLen;                  //接受包长度

    BOOL bRecPing;              //是否收到ping回复
	PPP_OPTION_T option[8];     //选项值
	IP_HEADER_T IP_header;
	ICMP_HEADER_T ICMP_header;
    int m_nNcpAckNum;
	
	CCRC m_crc;
};
#endif 

PPP_Packet.cpp

/* ***************************************************************
 * Filename:            Packet.cpp
 *  @Description:
 *         Packet Creat Check
 *  @Author: ybLin
 * ***************************************************************/
#include "stdafx.h"
#include "PPP_Packet.h"
#include "Common.h"


#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

int auth_skipped=1;
int block_ipcp_req= 0;

BYTE g_LcpFirstReq[256] = {0x7E, 0xFF, 0x03, 0xC0, 0x21, 0x01, 0x01, 0x00, 0x0A, 0x02, 0x06, 0x00, 0x00, 
                           0x00, 0x00, 0x58, 0x7B, 0x7E};
BYTE g_NcpFirstReq[256] = {0x7E, 0xFF, 0x03, 0x80, 0x21, 0x01, 0x03, 0x00, 0x0A, 0x03, 0x06, 0x00, 
                           0x00, 0x00, 0x00, 0xE9, 0xB3, 0x7E};

BYTE g_TermReq[256] = {0x7E, 0xFF, 0x03, 0xC0, 0x21, 0x05, 0x01, 0x00, 0x04, 0x3D, 0xC7, 0x7E};

char hextbl[] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };

//Common Api
std::string HexToString(const BYTE *pBuffer, size_t iBytes)
{
   std::string result;

   for (size_t i = 0; i < iBytes; i++)
   {
       BYTE c ;
       BYTE b = pBuffer[i] >> 4;
       if (9 >= b)
       {
           c = b + '0';
       }
       else
       {
           c = (b - 10) + 'A';
       }

       result += (TCHAR)c;

       b = pBuffer[i] & 0x0f;

       if (9 >= b)
       {
           c = b + '0';
       }
       else
       {
           c = (b - 10) + 'A';
       }

       result += (TCHAR)c;
       if (i != (iBytes-1))
           result += " ";
   }
   return result;
}

void DectoHex(int dec, char *hex, int length) 
{ 
	for(int i=length-1; i>=0; i--) 
	{ 
		hex[i] = (dec%256)&0xFF; 
		dec /= 256; 
	} 
}

unsigned long HextoDec(const unsigned char *hex, int length) 
{ 
	unsigned long rslt = 0; 
	for(int i=0; i<length; i++) 
	{ 
		rslt += (unsigned long)(hex[i])<<(8*(length-1-i)); 
	} 
	return rslt; 
} 

CString Ascii2Hex(CString strASCII)
{
	int i;
	int length = strASCII.GetLength();
	CString strHEX;
	CString temp;

	for (i = 0; i < length; i++)
	{
		temp.Format("%2hhX ", strASCII.GetAt(i));// %2hhX 解决出现的FFFFFF问题
		strHEX = strHEX + temp;
	}
	return strHEX;
}

//字符转换为16进制数据
char Char2Hex(char ch)
{

	if ((ch >= '0') && (ch <= '9'))
		return ch - 0x30;
	if ((ch >= 'A') && (ch <= 'F'))
		return ch - 'A' + 10;
	if ((ch >= 'a') && (ch <= 'f'))
		return ch - 'a' + 10;
	else
		return(-1);
}

//字符串转换为16进制数据
int String2Hex(CString str, char* SendOut)
{
	int hexdata, lowhexdata;
	int hexdatalen = 0;
	int len = str.GetLength();

	for (int i = 0; i < len;)
	{
		char lstr, hstr = str[i];
		if (hstr == ' ' || hstr == '\r' || hstr == '\n')
		{
			i++;
			continue;
		}
		i++;
		if (i >= len)
			break;
		lstr = str[i];
		hexdata = Char2Hex(hstr);
		lowhexdata = Char2Hex(lstr);
		if ((hexdata == 16) || (lowhexdata == 16))
			break;
		else
			hexdata = hexdata * 16 + lowhexdata;
		i++;
		SendOut[hexdatalen] = (char)hexdata;
		hexdatalen++;
	}
	return hexdatalen;

}

//Packet
CPacket::CPacket()
{
    InitPacket();
}

CPacket::~CPacket()
{
}

void CPacket::InitPacket()
{
    CurState = PPP_STATE_INIT;
	TxLen = 0;
	bReject = FALSE;
	PktID = 0x01;
    nOptionNum = 0;

	for(int i=0; i<8; i++)
	{
		option[i].bReject = TRUE;
		memset(option[i].bData, 0x0, 64);
	}
	memset(PacketTx,0x0,256);
	memset(SrcIP,0x00,4);
}

int CPacket::Parsepkt(BYTE *pPkt, int nLen)
{
	BYTE CodeVal;
	char tempbuf[100];
    memset(PacketRx, 0x00, 256);
	TxLen = 0;
    int nRet = PPP_OPERATE_NORMAL; 

    if(nLen < MIN_RECV_PKT_SIZE)
    {
        ffprint("nLen < MIN_RECV_PKT_SIZE nLen:%d", nLen);
        nRet = PPP_OPERATE_ERROR;
		return nRet;
    }

    RxLen = TransferDecode(pPkt, nLen, PacketRx);
    if(RxLen < MIN_RECV_PKT_SIZE)
	{
		ffprint("RxLen < MIN_RECV_PKT_SIZE RxLen:%d", RxLen);
        nRet = PPP_OPERATE_ERROR;
		return nRet;
	}

    std::string hexStr2 = HexToString(PacketRx, RxLen);
    ffprint("Parsepkt decode hexStr:%s", hexStr2.c_str());
    
	wProtocolType = PacketRx[3]*256 + PacketRx[4];
 
	switch(wProtocolType)
    {
	    case PROTOCOL_LCP:
		{
			CodeVal = PacketRx[5]; //获取编码值
			PktID = PacketRx[6];
			switch(CodeVal)
            {
    			case PPP_CODE_REQ:

    				CheckOption(wProtocolType, CodeVal, PacketRx);

                    if(bReject == TRUE)
                    {
                        CodeVal = PPP_CODE_REJ;
                    }
                    else 
                    { 
                        CodeVal = PPP_CODE_ACK;
                    }         
    				CreatePPPPkt(wProtocolType, CodeVal, PacketRx); 	
                    nRet = PPP_OPERATE_NEED_SEND;
    				break;
    			case PPP_CODE_ACK:
                    if(CurState == PPP_STATE_LCP_PASS)
                    {
                        //无认证,直接发送NCP请求
                        CurState = PPP_STATE_NCP_PERIOD;
                        CreatePPPPkt(PROTOCOL_IPCP, PPP_CODE_REQ, g_NcpFirstReq, TRUE);
                        nRet = PPP_OPERATE_NEED_SEND;
                        m_nNcpAckNum = 0;
                    }
    				break;
    			case PPP_CODE_NAK:
    				break;
    			case PPP_CODE_REJ:
    				break;
    			default:
    				break;
			}
			
			break;
		}
	    case PROTOCOL_IPCP:  //无转义
    		CodeVal = PacketRx[5];
    		PktID = PacketRx[6];
    		switch(CodeVal)
            {
        		case PPP_CODE_REQ:
                    ffprint("PROTOCOL_IPCP PPP_CODE_REQ");
                
                    CurState = PPP_STATE_NCP_PERIOD;
        		    CreatePPPPkt(wProtocolType, PPP_CODE_ACK, PacketRx);
                    nRet = PPP_OPERATE_NEED_SEND;
                    m_nNcpAckNum++;
        			break;
        		case PPP_CODE_ACK: //最终受到确认后,NCP通过
                    ffprint("PROTOCOL_IPCP PPP_CODE_ACK");
                
        			if (CurState < PPP_STATE_PPP_PASS)	
                        CurState = PPP_STATE_PPP_PASS;

                    m_nNcpAckNum = 0;

        			break;
        		case PPP_CODE_NAK:
                    ffprint("PROTOCOL_IPCP PPP_CODE_NAK");
                
        			CheckOption(wProtocolType, CodeVal, PacketRx);
                
        			if (CurState < PPP_STATE_NCP_NAK && bReject == false) 
                        CurState = PPP_STATE_NCP_NAK;

        			CreatePPPPkt(wProtocolType, PPP_CODE_REQ, PacketRx);
                    nRet = PPP_OPERATE_NEED_SEND;
        			break;
        		default:
        			break;
    		}
    		break;
    case PROTOCOL_IP:    
		IP_header.protocol = PacketRx[14];
        /*
		int k;
		k=16+1;
		IP_header.SrcIP[0] = pPkt[k++];
		IP_header.SrcIP[1] = pPkt[k++];
		IP_header.SrcIP[2] = pPkt[k++];
		IP_header.SrcIP[3] = pPkt[k++];
		IP_header.DesIP[0] = pPkt[k++];
		IP_header.DesIP[1] = pPkt[k++];
		IP_header.DesIP[2] = pPkt[k++];
		IP_header.DesIP[3] = pPkt[k++];*/

		switch(IP_header.protocol)
		{
    		case IP_ICMP:
    			ICMP_header.type = PacketRx[25];
                ffprint("PROTOCOL_IP protocol:%02x  type:%02x", IP_header.protocol, ICMP_header.type);
    			switch (ICMP_header.type)
    			{
        			case ICMP_PING:
        				//CreateIpPkt(IP_ICMP, ICMP_PINGREPLY, pPkt+5);
        				break;
        			case ICMP_PINGREPLY:
        				bRecPing = true;
        				break;
        			default:
        				break;
    			}
    			break;
    		case IP_UDP:
    			break;
    		default:
    			break;
		}
		break;

	default:
		break;
	}
	return nRet;
}

void CPacket::CheckOption(WORD wProType, BYTE CodeVal, BYTE* pOption)
{
	WORD wSize, wStart;
	int i=0, j=0, k;

	bReject = FALSE;
	wStart = 9; //数据位从这里开始
	wSize = pOption[7]*256 + pOption[8] + 8;            //8:length+framebegin(3)+protocol(2)+checksum(2)+frameend(1)
	if (wSize > MAX_RECV_PKT_SIZE - 8)                  //truncate packet if larger than buffer
        wSize = MAX_RECV_PKT_SIZE - 8;     

    //获取配置项
	for(k=0; k<8; k++)
	{
		option[k].bReject = TRUE;
		memset(option[k].bData, 0x0, 64);
	}

	while(wStart < wSize-3)
	{
		option[j].bType = pOption[wStart++];
		option[j].bLength = pOption[wStart++];
		for (i = 0; i<option[j].bLength-2; i++)
		{
			option[j].bData[i]=pOption[i+wStart];
		}
        
		option[j].bData[i]='\0';
		wStart = i + wStart;
		j++;
	}
	nOptionNum = j;

	switch(wProtocolType)
	{
		case PROTOCOL_LCP:
		{
			for(i=0; i<j; i++)
			{
				if (option[i].bType > 0 && option[i].bType <= 29)
				{
					switch(option[i].bType)
					{
                        //最大-接收-单元
						case 1:		
							option[i].bReject = FALSE;
							break;

                        //异步-控制-字符-映射
						case 2:		
							option[i].bReject = FALSE;
							break;

                        //鉴定-协议
						case 3:		
							option[i].bReject = TRUE;
							bReject = TRUE;
							break;

                        //魔术字
						case 5:		
							option[i].bReject = FALSE;
							break;

                        //协议域压缩
						case 7:		
							option[i].bReject = TRUE;
							bReject = TRUE;
							break;

                        //地址和控制域压缩
						case 8:		
							option[i].bReject = TRUE;
						    bReject = TRUE;
							break;

						default:
							option[i].bReject = TRUE;
							bReject = TRUE;
							break;
					}
				}
			}
			break;
		}

		case PROTOCOL_IPCP:
			for(i=0; i<j; i++)
			{
				if ((option[i].bType>0 && option[i].bType<5)||
                    (option[i].bType>=129 && option[i].bType<=132))
				{
					switch(option[i].bType)
					{
						case 1:		                                //静态IP配置
							option[i].bReject = TRUE;
							bReject = TRUE;
							break;
						case 2:		                                //IP压缩协议
							option[i].bReject = TRUE;
							bReject = TRUE;
							break;
						case 3:		                                //动态IP配置
							option[i].bReject = FALSE;
							if (CodeVal == PPP_CODE_NAK)
							{
								ffprint("NAK option[i].bLength:%d", option[i].bLength);
								for (k=0;k<option[i].bLength-2;k++)
								{
									SrcIP[k] = option[i].bData[k];
                                    ffprint("SrcIP[%d]:%02x", k, SrcIP[k]);
								}
							}
							break;
						default:    
							option[i].bReject = TRUE;
							bReject = TRUE;
							break;
					}
				}
			}
			break;
	}
}

uint16_t crc16_x25(uint8_t *data, int length){

    uint8_t i;
    uint16_t crc = 0xffff;        // Initial value
    while(length--)
    {
        crc ^= *data++;            // crc ^= *data; data++;
        for (i = 0; i < 8; ++i)
        {
            if (crc & 1)
                crc = (crc >> 1) ^ 0x8408;        // 0x8408 = reverse 0x1021
            else
                crc = (crc >> 1);
        }
    }
    return ~crc;                // crc^Xorout
} 


void CPacket::CreatePPPPkt(WORD wProType, BYTE CodeVal, BYTE* pPkt, bool bActive)
{
	int i,j,k;
	WORD wLen, whigh = 0, wlow = 0; //header checksum;
	int iHeadpos;
	WORD wChecksum;
    int nLenthPos;
	memset(PacketTx, 0x00, 256);  //有转义的包

	i=0;
	PacketTx[i++] = 0x7e;      //帧首个字节
	switch(wProType)
	{
		case PROTOCOL_LCP:
			switch(CodeVal)
			{
				case PPP_CODE_REQ:
					for(i=1; i<5; i++)
					{
						PacketTx[i] = pPkt[i];                 //frame head and protocol
					}
					
					PacketTx[i++] = CodeVal;                   //code
					PacketTx[i++] = PktID;                     //递增ID
					nLenthPos = i;                             //get the length postion
					i+=2;

                    if(bActive)                                //主动发送,数据域直接拷贝
                    {
                        while(pPkt[i] != 0x7e)                      
    					{
    						PacketTx[i] = pPkt[i];
    						i++;
    					}
    					i-=2;
    					PacketTx[i]='\0';
    					PacketTx[i+1]='\0';
                    }
                    else
                    {
                        //LCP配置项
    					for(k=0; k<nOptionNum; k++)
    					{
    						if(!option[k].bReject)
    						{
    							PacketTx[i++] = option[k].bType;
    							PacketTx[i++] = option[k].bLength;
    							for(j=0; j<option[k].bLength-2; j++)
    								PacketTx[i++] = option[k].bData[j];
    						}
    					}
                    }
                    CurState = PPP_STATE_LCP_PERIOD;
					break;

				case PPP_CODE_ACK:
				{
                    for(i=1; i<5; i++)
					{
						PacketTx[i] = pPkt[i];                  //frame head and protocol
					}
					PacketTx[i++] = CodeVal;                    //code
					int idIndex = i++;
					PacketTx[idIndex] = pPkt[idIndex];          //回复包的ID与请求包的ID一致	

					nLenthPos = i;                              //get the length postion
					i+=2;

					while(pPkt[i] != 0x7e)                      //数据域直接拷贝
					{
						PacketTx[i] = pPkt[i];
						i++;
					}
					i-=2;
					PacketTx[i]='\0';
					PacketTx[i+1]='\0';

                    CurState = PPP_STATE_LCP_PASS;
					break;
                }                    
                case PPP_CODE_REJ:
                    for(i=1; i<5; i++)
					{
						PacketTx[i] = pPkt[i];                 //frame head and protocol
					}
					
					PacketTx[i++] = CodeVal;                   //code
					PacketTx[i++] = PktID;                     //递增ID
					nLenthPos = i;                             //get the length postion
					i+=2;                           

                                                                
					for(k=0; k<nOptionNum; k++)                //LCP配置项 添加拒绝的配置项
					{
						if(option[k].bReject)
						{
							PacketTx[i++] = option[k].bType;
							PacketTx[i++] = option[k].bLength;
							for(j=0; j<option[k].bLength-2; j++)
								PacketTx[i++]=option[k].bData[j];
						}
					}
                    CurState = PPP_STATE_LCP_PERIOD;
                    break;

                case PPP_CODE_TERM_LINK:
                {
                    for(i=1; i<5; i++)
					{
						PacketTx[i] = pPkt[i];                  //frame head and protocol
					}
					PacketTx[i++] = CodeVal;                    //code
					PacketTx[i++] = PktID;                     //递增ID

					nLenthPos = i;                              //get the length postion
					i+=2;

					while(pPkt[i] != 0x7e)                      //数据域直接拷贝
					{
						PacketTx[i] = pPkt[i];
						i++;
					}
					i-=2;
					PacketTx[i]='\0';
					PacketTx[i+1]='\0';

                    CurState = PPP_STATE_TERM_LINK;
                    break;
                }      
			}
			break;
		case PROTOCOL_IPCP:
            switch(CodeVal)
            {
                case PPP_CODE_REQ:
                {
                    for(i=1;i<5;i++)
        		    {
        			     PacketTx[i] = pPkt[i];                 //frame head and protocol
        		    }
					
                    PacketTx[i++] = 0x01;                       //code
                    PacketTx[i++] = PktID++;                    //id 
                    nLenthPos = i;                              //get the length postion
                    i += 2;

                    if(bActive)                                //主动发送,数据域直接拷贝
                    {
                        while(pPkt[i] != 0x7e)                      
    					{
    						PacketTx[i] = pPkt[i];
    						i++;
    					}
    					i-=2;
    					PacketTx[i] = 0x00;
    					PacketTx[i+1] = 0x00;
                    }
                    else
                    {
                        if(CurState == PPP_STATE_NCP_NAK)
                        {
                            PacketTx[i++] = 0x03;
    						PacketTx[i++] = 0x06;
                    
                            ffprint("IPCP PPP_CODE_REQ nOptionNum:%d", nOptionNum);
                            for(k=0; k<nOptionNum; k++) 
                            {
                                if(!option[k].bReject)
                                {
                                    for(j=0;j<option[k].bLength-2;j++)
            						{
            							//ffprint("NAK SrcIP[%d]:%02x", j, SrcIP[j]);
        								PacketTx[i++] = SrcIP[j];
        							} 
                                }
                            }
                        }
                    }
                    
                    break;
                }   
                case PPP_CODE_ACK:
                {
                    for(i=1;i<5;i++)
        		    {
        			     PacketTx[i] = pPkt[i];                //frame head and protocol
        		    }
                    PacketTx[i++] = 0x02;                   //code
                    int idIndex = i++;
					PacketTx[idIndex] = pPkt[idIndex];         //回复包的ID与请求包的ID一致	
                    
                    nLenthPos = i;                             //get the length postion
					i+=2;
					while(pPkt[i] != 0x7e)                     //数据域直接拷贝
					{
						PacketTx[i] = pPkt[i];
						i++;
					}
					i-=2;
					PacketTx[i]=0x00;
					PacketTx[i+1]=0x00; 
                    break;
                }
                case PPP_CODE_NAK:
                    break;
                case PPP_CODE_REJ:
                    break;
                default:
                    break;

            }
        
			break;

		default:
			break;
	}

	wLen = i-5;    
    if(wProType == PROTOCOL_LCP)
    {
        PacketTx[nLenthPos++] = wLen/256;
	    PacketTx[nLenthPos] = wLen%256;
    }
    else
    {
        char Hight[2];
        char Low[2];
        DectoHex((int)(wLen/256), Hight, 1);
        DectoHex((int)(wLen%256), Low, 1);

        PacketTx[nLenthPos++] = (BYTE)Hight[0];
        PacketTx[nLenthPos++] = (BYTE)Low[0];

        ffprint("wLen:%d Hight:%02x, Low:%02x", wLen, Hight[0], Low[0]);
    }

	//CRC校验
    //CString strText;
    //strText.Format(_T("校验:%s"), HexToString(PacketTx+1, i-1));
    //ffprint("strText:%s", strText);

	wChecksum = m_crc.CountCRC(PacketTx+1, i-1);//get checksum and copy to szPacketTx[crcpos]
    //wChecksum=m_crc.CountCRC(TestByte, 14);

	//2. 
	PacketTx[i++]=(wChecksum & 0x00ff);
	PacketTx[i++]=((wChecksum>>8) & 0x00ff);

    if(wProType == PROTOCOL_LCP)
    {
        i = CharacterEncode(i);
    }
    
    PacketTx[i] = 0x7e;//and framing end 0x7e
	TxLen = i+1;
	PktID++;
	bReject = FALSE;
}

void CPacket::CreateIpPkt(BYTE protocal, BYTE type, BYTE *temp)
{
	int i,j,k;
    int nLenthPos, nHeadcheckpos, nIcmpcheckpos;
	WORD wLen,whigh = 0x0000,wlow = 0x0000;//header checksum;
	int iHeadpos,TTLpos;
	WORD wChecksum;
	
	memset(PacketTx, 0x00, 256);

    //7E FF 03 00 21
	i = 0;
	PacketTx[i++] = 0x7e;//frame header

	PacketTx[i++] = 0xff;
	PacketTx[i++] = 0x03;

    PacketTx[i++] = 0x00;
	PacketTx[i++] = 0x21;
	iHeadpos = i;

    //版本(4) + 首部长度(4) + 区分服务(8)
	PacketTx[i++] = 0x45;   //version and header length
	PacketTx[i++] = 0x00;   //service

    //总长度
	nLenthPos = i;			//total length position
	i += 2;

    //标识
    PacketTx[i++] = 0x00;
    PacketTx[i++] = 0x05;

    //标志(8) + 片偏移(8)
    PacketTx[i++]=0x00;
	PacketTx[i++]=0x00;

    //生存时间(8) + 协议(8)
	TTLpos=i;
	PacketTx[i++] = 0x80;//TTL
	PacketTx[i++] = protocal;//0x11:UDP 0x01:ICMP

    //IP 首部校验和
	nHeadcheckpos=i;
	PacketTx[i++]=0x00;//header checksum
	PacketTx[i++]=0x00;//header checksum

    //源IP
	for(j=0;j<4;j++)
	{
		PacketTx[i++]=SrcIP[j];//source ip address
	}

	switch(protocal)
	{
   
	case IP_ICMP:
		switch(type)
		{
		case ICMP_PING:

			//目标IP地址
			for(j=0; j<4; j++)
			{
				PacketTx[i++] = temp[j];
			}
		
			//类型 8位 ICMP type 0x08:请求 0x00:回复
			PacketTx[i++] = type;  

			//code 8位
			PacketTx[i++] = 0x00;//ICMP code

			//ICMP校验位和 16位
			nIcmpcheckpos = i;				
			PacketTx[i++] = 0x00;//icmp checksum set 0
			PacketTx[i++] = 0x00;//icmp checksum set 0

			//ICMP id 16位 ping进程的进程号
			PacketTx[i++] = 0x00;
			PacketTx[i++] = 0x01;

			//每个发送出去的分组递增序列号
			PacketTx[i++] = ICMP_header.sequence & 0x00ff;	//sequence number lsb
			PacketTx[i++] = ICMP_header.sequence >> 8;		//sequence number msb
			ICMP_header.sequence++;

			//数据部分
			k = temp[4]*256+temp[5];
			for (j=0; j<k; j++)
			{
				PacketTx[i++] = temp[6+j];
			}

			break;    
		default:
			break;
		}

        //ICMP 校验和计算
		wlow = 0;
		whigh = 0;
		for(j=nIcmpcheckpos-2; j<i; j+=2)
		{
			whigh += PacketTx[j];
		}
		for(j=nIcmpcheckpos-1; j<i; j+=2)
		{
			wlow += PacketTx[j];
		}

		//write the sub header checksum
		wlow += whigh/256;
		whigh = (whigh & 255)+wlow/256;
		wlow = (wlow & 255)+whigh/256;	
		whigh = ~whigh;
		wlow = ~wlow;
		PacketTx[nIcmpcheckpos++] = (whigh & 0x00ff);
		PacketTx[nIcmpcheckpos] = (wlow & 0x00ff);

		break;
		
	case IP_UDP:

        //目标IP + 源端口 + 目标端口 + 长度
		for(j=0; j<10; j++)
		{
			PacketTx[i++] = temp[j];
		}
    
        //UDP校验和
		nIcmpcheckpos = i;
		PacketTx[i++] = 0x00;
		PacketTx[i++] = 0x00;

        //UDP 发送的数据
		k = temp[8]*256+temp[9]+4;
		for(j=12;j<k;j++)
		{
			PacketTx[i++]=temp[j];
		}

        //计算UDP校验和
		wlow = 0x0000;
		whigh = 0x0000;
		for(j=nIcmpcheckpos-14; j<i; j+=2)
		{
			whigh += PacketTx[j];
		}
		for(j=nIcmpcheckpos-13; j<i; j+=2)
		{
			wlow += PacketTx[j];
		}
		whigh += temp[8];
		wlow += 0x11;
		wlow += temp[9];


		//write the sub header checksum
		wlow += whigh/256;
		whigh = (whigh & 255)+wlow/256;
		wlow = (wlow & 255)+whigh/256;	
		whigh = ~whigh;
		wlow = ~wlow;
		PacketTx[nIcmpcheckpos++] = (whigh & 0x00ff);
		PacketTx[nIcmpcheckpos] = (wlow & 0x00ff);


	default:
		break;
	}    
	wLen=i-5;
	PacketTx[nLenthPos++]=wLen/256;
	PacketTx[nLenthPos]=wLen%256;
	//计算IP校验和
	wlow=0;
	whigh=0;
	for(j=iHeadpos; j<20+iHeadpos; j+=2)
	{
		whigh += PacketTx[j];
	}
	for(j=iHeadpos+1; j<=20+iHeadpos; j+=2)
	{
		wlow += PacketTx[j];
	}
	wlow += whigh/256;
	whigh = (whigh & 255)+wlow/256;
	wlow = (wlow & 255)+whigh/256;
	whigh = ~whigh;
	wlow = ~wlow;
	PacketTx[nHeadcheckpos++] = (whigh & 0x00ff);
	PacketTx[nHeadcheckpos] = (wlow & 0x00ff);

    //计算CRC校验
	wChecksum = m_crc.CountCRC(PacketTx+1,i-1);//get checksum and copy to szPacketTx[crcpos]
	PacketTx[i++] = (wChecksum & 0x00ff);
	PacketTx[i++] = ((wChecksum>>8) & 0x00ff);

	PacketTx[i] = 0x7e;
	TxLen = i+1;
	PktID++;
	bReject = FALSE;
}

int CPacket::CharacterEncode(int iLen)
{
	BYTE temp,endtemp;
	int i,j;

	for(i=1;i<iLen;i++)
	{
		if(PacketTx[i]>=0x00 && PacketTx[i]<0x20)
		{
			endtemp=PacketTx[iLen++];
			for(j=iLen-1;j>i;j--)
			{
				temp=PacketTx[j];
				PacketTx[j+1]=temp;
			}
			temp=PacketTx[i]+0x20;
			PacketTx[i++]=0x7d;
			PacketTx[i]=temp;
			PacketTx[iLen]=endtemp;
		}
		else if(PacketTx[i]==0x7d)
		{
			endtemp=PacketTx[iLen++];
			for(j=iLen-1;j>i;j--)
			{
				temp=PacketTx[j];
				PacketTx[j+1]=temp;
			}
			temp=0x5d;
			PacketTx[i++]=0x7d;
			PacketTx[i]=temp;
			PacketTx[iLen]=endtemp;
		}
		else if(PacketTx[i]==0x7e)
		{
			endtemp=PacketTx[iLen++];
			for(j=iLen-1;j>i;j--)
			{
				temp=PacketTx[j];
				PacketTx[j+1]=temp;
			}
			temp=0x5e;
			PacketTx[i++]=0x7d;
			PacketTx[i]=temp;
			PacketTx[iLen]=endtemp;
		}
	}
	return(iLen);
}

int CPacket::TransferEncode(unsigned char *pInPkt, int nInLen, unsigned char *pOutPkt)
{
    unsigned char *pIn, *pOut;
    int i, nTmpLen;
    
    pIn = pInPkt;
    pOut = pOutPkt;
    nTmpLen = nInLen;
	int nOutLen = 0;
    
    for(i = 0; i < nInLen; i++)
    {
        if(*pIn == PPP_FRAME_FLAG || *pIn == PPP_FRAME_ESC || *pIn < 0x20)
        {
            *pOut = PPP_FRAME_ESC;
            pOut++;
            nTmpLen++;    
            *pOut = *pIn ^ PPP_FRAME_ENC;
        }
        else
        {
            *pOut = *pIn;    
        }
        
        pIn++;
        pOut++;
    }
    nOutLen = nTmpLen;
    
    return nOutLen;    
}

int CPacket::TransferDecode(unsigned char *pInPkt, int nInLen, unsigned char *pOutPkt)
{
    unsigned char *pIn, *pOut;
    int i, nTmpLen;
    
    pIn = pInPkt;
    pOut = pOutPkt;
    nTmpLen = nInLen;
	int nOutLen = 0;
     
   	for(i = 0; i < nInLen; i++)
    {
        if(*pIn == PPP_FRAME_ESC)
        {
            pIn++;
           	nTmpLen--;
            *pOut = *pIn ^ PPP_FRAME_ENC;
            
            i++;
        }    
        else
        {
            *pOut = *pIn;    
        }
        
        pIn++;
        pOut++;
    }
    nOutLen = nTmpLen;
    
    return nOutLen;
}

void CPacket::WriteLog(CString temp)
{
	CStdioFile file;
	CString filename;
	CTime time;
	time = CTime::GetCurrentTime();
	filename = time.Format("%Y%m%d");
	file.Open(filename+".log",CFile::modeNoTruncate|CFile::modeCreate|CFile::modeWrite|CFile::typeText);
	file.SeekToEnd();
	temp = time.Format("%Y.%m.%d %H:%M:%S  ")+temp+"\r\n";
	file.Write(temp,temp.GetLength());
	file.Close();
}

;