前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >PPTP协议详解及报文解析

PPTP协议详解及报文解析

作者头像
用户6280468
发布于 2022-03-21 09:03:35
发布于 2022-03-21 09:03:35
4.9K00
代码可运行
举报
文章被收录于专栏:txp玩Linuxtxp玩Linux
运行总次数:0
代码可运行

PPTP是点对点隧道协议,建立在PPP协议上的V**隧道技术。它已有20多年的历史。目前有这些广泛使用的V**协议。主要是PPTP、L2TP、IPsec、OpenV**、SSTP、IKEv2等。PPTP协议依赖于加密,认证和端对端协议(PPP)进行协商。实质上,它只需要用户名,密码和服务器地址就可创建连接。

pptp协议

PPTP协议不是IETF建议的标准,是由微软、3Com等厂商联合形成的产业联盟开发。1999年7月发布的 RFC 2637是第一个正式的PPTP规格书。

PPTP在控件上定义了一组作为TCP数据发送的消息PNS和给定PAC之间的连接。TCP会话通过启动与以下设备的TCP连接来建立控制连接端口1723 。源端口分配给任何未使用的端口号。每个PPTP控制连接消息均以固定的8个八位位组开头标头部分。此固定的标头包含以下内容:总计消息的长度,PPTP消息类型指示符和“Magic Cookie”

Magic Cookie始终作为常量0x1A2B3C4D发送。它的基本目的是让接收器确保其正确与TCP数据流同步。它不应该用作在以下情况下重新同步TCP数据流的方法:发送方发出格式错误的消息。的损失同步必须导致控件立即关闭连接的TCP会话。

为了清楚起见,下一个中的所有“控制连接”消息模板部分包括整个PPTP控制连接消息头。以0x开头的数字是十六进制值。

Start-Control-Connection-Request 1 Start-Control-Connection-Reply 2 Stop-Control-Connection-Request 3 Stop-Control-Connection-Reply 4 Echo-Request 5 Echo-Reply 6 (Call Management) Outgoing-Call-Request 7 Outgoing-Call-Reply 8 Incoming-Call-Request 9 Incoming-Call-Reply 10 Incoming-Call-Connected 11 Call-Clear-Request 12 Call-Disconnect-Notify 13 WAN-Error-Notify 14 Set-Link-Info 15

开始控制连接请求及报文格式

Start-Control-Connection-Request是使用的PPTP控制消息在PNS和PAC之间建立控制连接。每PNS-PAC对需要专用的控制连接成立。必须先建立控制连接可以发出其他PPTP消息。建立控制连接可以由PNS或PAC发起。

开始控制连接请求,用于初始化PPTP Client 和Server之间的Tunnel,开始Tunnel的建立过程。

Start-Control-Connection-Request报文格式

Length:此PPTP的总长度(以八位字节为单位)消息,包括整个PPTP标头。

PPTP Message Type :1为控制消息。

Magic Cookie:0x1A2B3C4D。使用该常数作为对收到的邮件的完整性检查。

Control Message Type:Start-Control-Connection-Request的控制消息类型1。

Reserved0 :该字段必须为0。

Protocol Version :PPTP协议的版本发件人希望使用。

Reserved1:该字段必须为0。

Framing Capabilities :该消息的发送者可以提供的信息。 当前定义的位设置为:1-支持异步帧,2-支持同步帧

Bearer Capabilities :该消息发送者的能力可以提供。当前定义的位设置是:1-支持模拟访问2-支持数字访问。

Maximum Channels:单个PPP会话总数该PAC可以支持。在开始控制-由PNS发出的连接请求,此值应设置为0。必须为被PAC忽略。

Firmware Revision:该字段包含固件版本签发PAC的编号(由签发)PAC或PNS PPTP的版本驱动程序。

Host Name :一个64字节的字段,其中包含DNS名称发行PAC或PNS。如果小于64八位字节,其余的字段应填充八位字节的值0。

Vendor Name :包含供应商的64个八位位组字段描述PAC类型的特定字符串正在使用或PNS软件的类型如果此请求是由PNS。如果长度少于64个八位位组,则该字段的其余部分应填写八位字节的值为0。

开始控制连接回复

Start-Control-Connection-Reply是在其中发送的PPTP控制消息回复收到的开始控制连接请求消息。这个消息包含指示控件结果的结果代码连接建立尝试。

开始控制连接回复,表示接受了对端的连接请求,Tunnel的建立过程可以继续。

Start-Control-Connection-Reply报文格式

Start-Control-Connection-Reply的控制消息类型2。

Outgoing-Call-Request

Outgoing-Call-Request是PNS发送的PPTP控制消息到PAC,以指示从PAC发出的去电成立。该请求向PAC提供了所需的信息拨打电话。它还向PAC提供以下信息:用于规范此会话向PNS的数据传输一旦建立。

PPTP Client发Outgoing Call Request,创建隧道,选择一个用以从客户端到服务器发送数据的PPTP隧道进行标识作用的调用ID。

Outgoing-Call-Request报文格式

Control Message Type:控制消息类型 7

Call ID:唯一标识符,对于特定对象是唯一的PNS分配给此的PAC-PNS对会议。它用于多路复用和解复用通过隧道发送的数据在PNS和PAC之间会议。

Call Serial Number:PNS为其分配的标识符会议,以识别此目的记录的会话中的特定会话信息。与“CALL ID”不同,PNS和PAC关联相同的呼叫序列给定会话的编号。这个组合IP地址和呼叫序列号独一无二。

Minimum BPS :最低可接受的线速度(以位/秒)。

Maximum BPS :最高可接受线速度(以位/秒)。

Bearer Type :指示承载能力的值此拨出电话所需。的当前定义的值为: 1-拨打模拟电话渠道 2-拨打数字电话渠道 3-可以拨打任何类型的电话渠道。

Framing Type :指示PPP帧类型的值用于此拨出电话。 1-调用以使用异步帧 2-调用以使用同步帧 3-通话可以使用以下任一类型框架。

Packet Processing Delay :数据包处理延迟数据包处理延迟的度量可能会强加给发送到PAC的PNS。指定该值以1/10秒为单位。对于PNS来说数量应该很小。

Phone Number Length :实际有效位数。

Reserved1 :该字段必须为0。

Phone Number :建立该号码所要拨打的号码传出会话。用于ISDN和模拟呼叫此字段是ASCII字符串。如果电话数字长度小于64个八位位组,该字段的其余部分填充为值的八位字节。

Subaddress:一个64字节的字段,用于指定其他拨号信息。如果子地址是长度少于64个八位位组,其余此字段填充值为0的八位位组。

Outgoing-Call-Reply

Outgoing-Call-Reply是PAC发送到的PPTP控制消息响应于接收到的呼出请求消息的PNS。的回复表示呼出尝试的结果。它也是向PNS提供有关用于特定参数的信息电话。它提供信息以允许PNS规范将数据传输到此会话的PAC。

PPTP Server回Outgoing Call Reply,隧道创建成功应答。选择一个用以从服务器到客户端发送数据的PPTP隧道进行标识作用的调用ID。

Outgoing-Call-Reply报文格式

Result Code:目前有效值为: 1(已连接)-呼叫建立于没有错误 2(一般错误)-未拨出电话根据指示的原因建立错误代码 3(无运营商)-拨出电话失败由于未检测到载体 4(忙)-由于以下原因,去电失败检测忙音 5(无拨号音)-拨出电话由于缺少拨号音而失败 6(超时)-未拨出电话在规定的时间内建立PAC 7(不接受)-拨出电话行政上禁止

Set-Link-Info

设置链接信息消息是由PNS发送的PPTP控制消息到PAC来设置PPP协商的选项。因为这些选项可以在通话期间的任何时间更改,PAC必须能够动态更新其内部呼叫信息并执行PPP在活动的PPP会话中进行协商。

PPTP Client 发送一条Set-Link-Info 指定PPP协商选项。到此为止,PPTP的控制层连接就已经建立起来了。

Set-Link-Info报文格式

Send ACCM :客户端应使用的发送ACCM值处理传出的PPP数据包。默认值客户在此消息之前使用的值收到的是0XFFFFFFFF。

Receive ACCM :客户端应使用的接收ACCM值处理传入的PPP数据包。默认值客户在此消息之前使用的值收到的是0XFFFFFFFF。

PPTP报文解析代码实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/stat.h>
#include <sys/types.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define MAGIC_COOKIE    0x1A2B3C4D

#define VENDOR_NAME     64

#define CNTRL_REQ       0x01
#define CNTRL_REPLY     0x02
#define STOP_REQ        0x03
#define STOP_REPLY      0x04
#define ECHO_REQ        0x05
#define ECHO_REPLY      0x06
#define OUT_REQ         0x07
#define OUT_REPLY       0x08
#define IN_REQ          0x09
#define IN_REPLY        0x0A
#define IN_CONNECTED    0x0B
#define CLEAR_REQ       0x0C
#define DISC_NOTIFY     0x0D
#define ERROR_NOTIFY    0x0E
#define SET_LINK        0x0F

static void dissect_cntrl_req(u_char *pptp_data, int offset)
{

    offset += ; /*Protoctol version*/

    offset += ;/*Reserved*/

    offset += ;/*Framing Capabilities*/

    offset += ;/*Barer Capabilities */

    offset += ;/*Maximum Channels*/

    offset += ;/*Firware Revision*/

    offset += ;/*Host Name*/

    char *pszInfoBuf = (char*)malloc();
    if (pszInfoBuf != )
    {
        memcpy(pszInfoBuf, pptp_data+offset,VENDOR_NAME);
        pszInfoBuf[VENDOR_NAME] = '\0';
        printf("Vendor Name: %s\n",pszInfoBuf);
    }
}

static void dissect_cntrl_reply(u_char *pptp_data, int offset)
{

    offset += ; /*Protoctol version*/

    offset += ; /*Result Code*/

    offset += ; /*Error Code*/

    offset += ; /*Framing Capabilities*/

    offset += ;/*Barer Capabilities */

    offset += ;/*Maximum Channels*/

    offset += ;/*Firware Revision*/

    offset += ;/*Host Name*/

    char *pszInfoBuf = (char*)malloc();
    if (pszInfoBuf != )
    {
        memcpy(pszInfoBuf, pptp_data+offset,VENDOR_NAME);
        pszInfoBuf[VENDOR_NAME] = '\0';
        printf("Vendor Name: %s\n",pszInfoBuf);
    }
}

static void dissect_out_req(u_char *pptp_data, int offset)
{

    offset += ;/*Call ID*/

    offset += ;/*Call Serial*/

    offset += ;/*Minimum BPS*/

    offset += ;/*Maximum BPS*/

    offset += ;/*Bearer Type*/

    offset += ;/*Framing Type*/

    offset += ;/*Packet Receive Window Size*/

    offset += ;/*Packet Processing Delay*/

    offset += ;/*Phone Number Length*/

    offset += ;/*Reserved*/

    offset += ;/*Phone Number*/

}

static void dissect_out_reply(u_char *pptp_data, int offset)
{

    offset += ;/*Call ID*/

    offset += ;/*Peer ID*/

    offset += ;/*Result Code*/

    offset += ;/*Error Code*/

    offset += ;/*Cause Code*/

    offset += ;/*Connect Speed*/

    offset += ;/*Packet Receive Window Size*/

    offset += ;/*Packet Processing Delay*/


}

static void dissect_set_link(u_char *pptp_data, int offset)
{
    offset += ; /*Reserved*/

    offset += ; /*Send ACCM*/

    offset += ; /*Receive ACCM*/
}

void dissect_pptp(u_char *pptp_data)
{

    int offset = ;

    uint32_t mangic =  ;

    offset += ; /*Length*/

    offset += ; /*Message type*/

    mangic =  ntohl(*(uint32_t*)(pptp_data+offset));
    printf("mangic = 0x%X\n",mangic);
    if (mangic == MAGIC_COOKIE)
        printf("correct\n");
    else
        printf("incorrect");
    offset += ; /*Magic Cookie*/

    uint16_t control_message_type = ntohs(*(uint16_t*)(pptp_data+offset));
    printf("control_message_type = 0x%X\n",control_message_type);
    offset += ; /*Control Message Type*/

    offset += ; /*Reserved*/

    switch(control_message_type)
    {
        case CNTRL_REQ: /* Start-Control-Connection-Request */
            dissect_cntrl_req(pptp_data, offset);
          break;
        case CNTRL_REPLY: /* Start-Control-Connection-Reply */
            dissect_cntrl_reply(pptp_data, offset);
          break;
        case STOP_REQ: /* Stop-Control-Connection-Request */

          break;
        case STOP_REPLY: /* Stop-Control-Connection-Reply */

          break;
        case ECHO_REQ: /* Echo-Request */

          break;
        case ECHO_REPLY: /* Echo-Reply */

          break;
        case OUT_REQ: /* Outgoing-Call-Request */
            dissect_out_req(pptp_data, offset);
          break;
        case OUT_REPLY: /* Outgoing-Call-Reply */
            dissect_out_reply(pptp_data, offset);
          break;
        case IN_REQ: /* Incoming-Call-Request */

          break;
        case IN_REPLY: /* Incoming-Call-Reply */

          break;
        case IN_CONNECTED: /* Incoming-Call-Connected */

          break;
        case CLEAR_REQ: /* Call-Clear-Request */

          break;
        case DISC_NOTIFY: /* Call-Disconnect-Notify */

          break;
        case ERROR_NOTIFY: /* WAN-Error-Notify */

          break;
        case SET_LINK: /* Set-Link-Info */
            dissect_set_link(pptp_data, offset);
          break;
        default: /* Unknown Type... */

          break;
    }

}


int pkt_number = ;
void ace_pcap_hand(u_char *par, struct pcap_pkthdr *hdr, u_char *data)
{

    int offset = ;
    char *pszInfoBuf = NULL;

    pkt_number++;
    struct ether_header *pEther = (struct ether_header *)data;/*以太网帧头*/
    if (pEther->ether_type != ntohs(ETHERTYPE_IP))/*仅处理IP数据包*/
        return;
    struct ip *pIpHdr = (struct ip*)(pEther+1);

    //仅处理TCP数据包 
    if (pIpHdr->ip_p != IPPROTO_TCP)
        return;    
    printf("----------pkt %d-----------\n", pkt_number);

    int iHeadLen = pIpHdr->ip_hl*;
    printf("Header Length %d\n",iHeadLen);

    int iPacketLen = ntohs(pIpHdr->ip_len) - iHeadLen;  
    printf("iPacketLen %d\n",iPacketLen);

    struct tcphdr *pTcpHdr = (struct tcphdr *)(((char  *)pIpHdr) + iHeadLen);

    int iPayloadLen = iPacketLen - pTcpHdr->doff*;
    printf("TCP Payload Len %d\n",iPayloadLen);


    u_char *pptp_data = (u_char*)(pTcpHdr+);

    dissect_pptp(pptp_data);

}

int main(int argc, char* argv[])
{
    char errbuf[];
    pcap_t *desc = ;

    char *filename = argv[];
    if (argc != )
    {
        printf("usage: ./pptp_test [pcap file]\n");
        return -1;
    }

    printf("ProcessFile: process file: %s\n", filename);
    if ((desc = pcap_open_offline(filename, errbuf)) == NULL)
    {   
        printf("pcap_open_offline: %s error!\n", filename);
        return -1; 
    }   

    pcap_loop(desc, pkt_number, (pcap_handler)ace_pcap_hand, NULL);
    pcap_close(desc);
    return ;
}

总结

PPTP控制连接建立在PPTP客户机IP地址和PPTP服务器IP地址之间,PPTP客户机使用动态分配的TCP端口号,而PPTP服务器则使用保留TCP端口号1723。

在V**协议中PPTP是最快的协议,主要用于流媒体和游戏中。本篇主要大致分析下客户端和服务端的报文、详细分析数据包的内容。对报文进行解析代码实现。

参考:https://www.rfc-editor.org/rfc/rfc2637.txt

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 txp玩Linux 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验