Gurdeep Singh Pall
Jeff Taarud
Kory Hamzeh
William Verthein
W. Andrew Little
February 22, 1996
1.0 Introduction
2.0 Protocol
3.0 PPTP Packet Format
Appendix A. Acknowledgment Time-Outs
Appendix B. Acknowledgment Time-Out and Window Adjustment
Appendix C. PPTP State Diagrams
Messages:
Start Session Request
Start Session Reply
Stop Session Request
Stop Session Reply
Echo Request
Echo Reply
Outgoing Call Request
Outgoing Call Reply
Incoming Call Request
Incoming Call Reply
Incoming Call Connected
Clear Call Request
Call Disconnected Notify
WAN Error Notify
Set Link Info
This document describes the Point-to-Point Tunneling Protocol (PPTP) developed by the PPTP forum. The forum consists of the following organizations: Ascend Communications, Microsoft Corporation, 3Com/Primary Access, ECI-Telematics, and U.S. Robotics.
The PPTP protocol is designed to perform the following tasks:
There are two basic packet types: data packets and control packets. Data packets contain user data that must be sent to the WAN or was received from the WAN. Data packets are PPP packets encapsulated using the Internet Generic Routing Encapsulation protocol, version 2 (GRE v2).
Control packets are used strictly for status inquiry and signaling information. Control packets are transmitted and received over a TCP connection. Each Windows NT Server and FEP pair will have exactly one TCP connection used for the control channel.
The PPTP protocol consists of a fixed packet header, followed by a variable-length protocol data portion (based on the command type).
All fields are in network byte order (big endian).
Here are some global definitions of semi-useful information.
/* * PPTP Well Known TCP Port number */ #define PPTP_TCP_PORT_NUMBER 5678 /* * The Maximum MTU for User Data. */ #define PPTP_MAX_DATA_MTU 1532 /* * PPTP Protocol Version and Revision. The high byte is * the version number, and the low byte is the revision * number. */ #define PPTP_PROTOCOL_VERSION 0x0100 /* * The PPTP SessionID used internally. It is essentialy the * IP Address of the remote host. */ typedef struct { IpAddress id; } PptpSessionID;
There are two different PPTP packet types:
The PPTP packet format is as follows:
/* * PPTP Packet Types. * * PPTP_CONTROL_PACKET: This packet contains signalling * information. * * PPTP_MGMT_PACKET: This packet contain remote * management data. */ typedef enum { PPTP_CONTROL_PACKET = 1, PPTP_MGMT_PACKET } PptpPacketType; #define PPTP_MAGIC_COOKIE 0x1a2b3c4d /* * The PPTP Header. * * This is the command header for all PPTP packets. * * packetLength: Total length of packet including * the PptpPacketHeader. * * packetType: One of PptpPacketType. * * magicCookie: Must be 0x1a2b3c4d. */ typedef struct { Word packetLength; Word packetType; LongWord magicCookie; } PptpPacketHeader; /* * Number of seconds of inactivity to wait before sending a * PPTP_ECHO_REQUEST message. */ #define PPTP_ECHO_ACTIVITY_SECONDS (60) /* * Number of bytes reserved for the "hostname" field used in PPTP * messages */ #define MAX_HOSTNAME_LENGTH (64) /* * Number of bytes reseverd for the "vendorString" field used in * PPTP messages */ #define MAX_VENDOR_STRING_LENGTH (64) /* * Number of bytes reserved for all variants of call addresses * used in PPTP messages */ #define MAX_PHONE_NUMBER_LENGTH (64) /* * Number of bytes reserved for the "callStatistics" field used in * PPTP messages */ #define MAX_CALL_STATS_LENGTH (128) /* * Number of seconds to wait for a PPTP_ECHO_REPLY response to a * PPTP_ECHO_REQUEST before timing out the control connection */ #define PPTP_ECHO_REPLY_TIMEOUT_SECONDS (60) /* * Number of seconds to wait before a new PPTP_WAN_ERROR_NOTIFY * message can be sent to a server for a particular call */ #define PPTP_WAN_ERROR_NOTIFY_RETRANSMIT_SECONDS (60)
The PPTP control message header is very simple. The only information it contains is the message type, which is one of the following:
/* * The PPTP Control Message Types. * */ typedef enum { PPTP_START_SESSION_REQUEST = 1, PPTP_START_SESSION_REPLY, PPTP_STOP_SESSION_REQUEST, PPTP_STOP_SESSION_REPLY, PPTP_ECHO_REQUEST, PPTP_ECHO_REPLY, PPTP_OUT_CALL_REQUEST, PPTP_OUT_CALL_REPLY, PPTP_IN_CALL_REQUEST, PPTP_IN_CALL_REPLY, PPTP_IN_CALL_CONNECTED, PPTP_CALL_CLEAR_REQUEST, PPTP_CALL_DISCONNECT_NOTIFY, PPTP_WAN_ERROR_NOTIFY, PPTP_SET_LINK_INFO, PPTP_NUMBER_OF_CONTROL_MESSAGES } PptpControlMessageType;
The following values are PPTP general error codes:
/* * General Error Codes. Each command, when applicable, has a * "resultCode" and "generalErrorCode" field. If the resultCode * field is set to *_GENERAL_ERROR, then the generalErrorCode field * must be examined to determine the true error code. * * PPTP_NOT_CONNECTED: We have not done a full handshake * yet. Can't accept this command. * * PPTP_BAD_FORMAT: Command length is wrong. * * PPTP_BAD_VALUE: One of the field values was out * of range. * * PPTP_NO_RESOURCE: Can't deal with this command * right now. * * PPTP_BAD_CALLID: The callID is invalid for this * context. * * PPTP_REMOTE_DEVICE_ERROR: Generic vendor-specific error * occurred on the FEP. */ typedef enum { PPTP_ERROR_CODE_NONE = 0, PPTP_NOT_CONNECTED = 1, PPTP_BAD_FORMAT, PPTP_BAD_VALUE, PPTP_NO_RESOURCE, PPTP_BAD_CALLID, PPTP_REMOTE_DEVICE_ERROR } PptpGeneralError; The format for the Control message header is: /* * The PPTP Control Header * * messageType: One of PptpControlMessageType * that indicates which control * message is being sent. * */ typedef struct { Word messageType; Word reserved; } PptpControlHeader;
This message is used to start a control channel between peers. This must be the first control message sent from one peer to another once the TCP connection reaches the ESTABLISHED state.
/* * The following constants are bit fields that define the * framing capabilities of the sender of the StartSessionRequest * and StartSessionReply messages. These values are stored * in the "framingCapability" field of both messages. * * PPTP_FRAME_CAP_ASYNC: Can do async PPP framing. * * PPRP_FRAME_CAP_SYNC: Can do sync PPP framing. */ #define PPTP_FRAME_CAP_ASYNC 0x00000001L #define PPTP_FRAME_CAP_SYNC 0x00000002L /* * The following constants are bit fields that define the * bearer capabilities of the sender of the StartSessionRequest * and StartSessionReply messages. These values are stored * in the "bearerCapability" field of both messages. * * PPTP_BEARER_CAP_ANALOG: Can do analog calls. * * PPTP_BEARER_CAP_DIGITAL: Can do digital calls. */ #define PPTP_BEARER_CAP_ANALOG 0x00000001L #define PPTP_BEARER_CAP_DIGITAL 0x00000002L
The format of this message is:
/* * The PPTP Start Session Control Message. This message can be sent * by the Windows NT Server or the FEP to start a control * channel. A control channel must be opened before any other * commands can be issued. * * protocolVersion: The Protocol Version that I speak. * * framingCapability: Describes what type of framing * sender of this message can support. * See PPTP_FRAME_CAP_* above. * * bearerCapability: Describes what type of bearer * capabilties is available to the * sender of this message. See * PPTP_BEARER_CAP_* above. * * maxChannel: Total number of channel this box * can handle. Valid only if the * FEP initiates the session. * * * firmwareRevision: The revision of the operating * firmware of the sender of this * message. This field contains the * firmware revision if the FEP * send this message, or the driver * version if the NT server sends this * message. * * hostName: My host name. * * vendorString: A sender-specific ASCII string * to describe the sender's equipment. */ typedef struct { Word protocolVersion; Byte reserved1; Byte reserved2; LongWord framingCapability; LongWord bearerCapability; Word maxChannels; Word firmwareRevision; Byte hostName[ 64 ]; Byte vendorString[ 64 ]; } PptpStartSessionRequest;
3.3.1.1.1 A Note About Start Session Collisions
A Start Session collision can happen when the two endpoints (the Windows NT Server and the FEP) attempt to open a session simultaneously. In such a case, arbitration is done by comparing the IP addresses of the endpoint. The peer with the higher IP address wins. For example, in a collision between 192.33.45.17 and 192.33.45.89, .89 will be the winner. The TCP connection initiated by the loser must be closed, and the loser must send a Start Session Reply to the winner on the TCP connection initiated by the winner. The TCP connection initiated by the loser must be closed by both sides.
This message is the result of the Start Session Request message. The result code can be one of the following:
/* * The possible result codes returned by the Comm Server in the * StartSessionReply message. * * PPTP_START_OK: Session opened succesfully. * * PPTP_START_ALREADY_CONNECTED: I already have an open * connection with you. * * PPTP_START_NOT_AUTHORIZED: I've been told I can't talk to you. * * PPTP_START_UNKNOWN_PROTOCOL: I don't know the protocol version * that you know. */ typedef enum { PPTP_START_OK = 1, PPTP_START_GENERAL_ERROR = 2, PPTP_START_ALREADY_CONNECTED, PPTP_START_NOT_AUTHORIZED, PPTP_START_UNKNOWN_PROTOCOL, } PptpStartSessionResultCode;
The packet format for the Start Session Reply is:
/* * The PPTP Start Session Reply Message. This message is sent as * a reply to the StartSessionRequest. * * protocolVersion: The PPTP protocol version being * run by the sender of this message. * * * resultCode: One of PptpStartSessionResultCode. * * generalErrorCode: Valid resultCode is * PPTP_START_GENERAL_ERROR. * * framingCapability: Describes what type of framing * sender of this message can support. * See PPTP_FRAME_CAP_* above. * * bearerCapability: Describes what type of bearer * capabilties is available to the * sender of this message. See * PPTP_BEARER_CAP_* above. * * maxChannels: Total number of channels the * sender can handle. The receiver * of the message should not make * any assumptions on channel * availability at any point in time. * * firmwareRevision: The revision of the operating * firmware of the sender of this * message. * * hostName: The hostname of the sender of * this message. * * vendorString: A sender-specific ASCII string * to describe the sender's equipment. * This string must be NULL-terminated. * */ typedef struct { Word protocolVersion; Byte resultCode; Byte generalErrorCode; LongWord framingCapability; LongWord bearerCapability; Word maxChannels; Word firmwareRevision; Byte hostName[ 64 ]; Byte vendorString[ 64 ]; } PptpStartSessionReply;
3.3.1.2.1 A Note About Protocol Versions
Special attention must be given to the protocol version field during the Session Start Request and Reply phase. Each peer must examine the protocol version field and must "step down" to the lowest common version of the protocol. If the receiver of the Start Reply (that is, the sender of the Start Request) cannot or is not willing to step back, it must close the session by sending a Stop Session Request with the reason field set to PPTP_STOP_PROTOCOL. If the receiver of the Start Request cannot is not willing to step back, it must send a Start Session Reply with an error code of PPTP_START_UNKNOWN_PROTOCOL.
The Stop Session Request message is used when one end does not need/want to talk to the peer and wants to close the control channel. All active calls are implicitly cleared.
/* * Possible reasons for stopping a session. * * PPTP_STOP_NONE: I felt like it. * * PPTP_STOP_PROTOCOL: Can't support your version of the * PPTP protocol. * * PPTP_STOP_LOCAL_SHUTDOWN: I am being shut down. */ typedef enum { PPTP_STOP_NONE = 1, PPTP_STOP_PROTOCOL, PPTP_STOP_LOCAL_SHUTDOWN } PptpStopReasons; /* * The PPTP StopSessionRequest. * * This command will clear all calls and listens and close the * control channel. * * reason: One of PptpStopReasons. */ typedef struct { Byte reason; } PptpStopSessionRequest;
This command is the reply to the Stop Session Request. The possible result codes are:
/* * The SessionStopReply Result Code. * * PPTP_STOP_OK: Control channel closed. * * PPTP_STOP_GENERAL_ERROR: See the generalErrorCode field. */ typedef enum { PPTP_STOP_OK = 1, PPTP_STOP_GENERAL_ERROR = 2 } PptpStopSessionResultCode; /* * The PPTP SessionStopReply packet. * * resultCode: One of PptpStopSessionResultCode * listed above. * * generalErrorCode: Contains the general error if * resultCode is *_GENERAL_ERROR. */ typedef struct { Byte resultCode; Byte generalErrorCode; } PptpStopSessionReply;
The Echo Request can be sent by either peer to the other. This command is to be used as a "keep-alive" type command. This packet must be sent by either end after 60 seconds of no activity. If an Echo Reply is not received within 60 seconds, the control channel must be shut down.
The format of the command is:
/* * The Echo-Request packet format. * * identNumber: This number is set by the sender of the * echo request. It is used to match up * replies with requests. */ typedef struct { LongWord identNumber; } PptpEchoRequest;
This packet is sent as a response to an Echo Request.
The format for this packet is:
/* * PptpEchoReplyResultCode contains the specific result codes * for the resultCode field in the PPTP_ECHO_REPLY message * * PPTP_ECHO_OK The echo reply is valid. * * PPTP_ECHO_GENERAL_ERROR A general error has occurred; * look at the generalErrorCode * field to determine the error. */ typedef enum { PPTP_ECHO_OK = 1, PPTP_ECHO_GENERAL_ERROR } PptpEchoReplyResultCode; /* * The Echo-Reply packet format. * * identNumber: Must match the indentNumber of the Echo * request. * * resultCode: If non-zero, then generalErrorCode * indicates some sort of error. * * generalErrorCode: Contains the general error if non-zero. */ typedef struct { LongWord identNumber; Byte resultCode; Byte generalErrorCode; Word reserved; } PptpEchoReply;
The sequence of packets for incoming and outgoing calls is shown in Figure 1.
Figure 1. Sequence of packets for incoming and outgoing calls
The Incoming Call Request message is generated by the FEP to indicate an incoming call (ring) immediately following the ring or after the FEP has processed the user information. If the NTS wants to answer the call, it sends an Incoming Call Reply message. This instructs the FEP to answer the call and negotiate the connect speed. After the FEP makes the connection, it can determine (based on the connect speed) how large a receive window it wants and how long it will take to process the data it does receive. The amount of time required to process data is called the Packet Processing Delay (PPD). The FEP responds back with an Incoming Call Connected message containing its receive window size and PPD value.
The Outgoing Call Request message is generated by NTS to initiate a call. After the FEP makes the connection, it can determine (based on the connect speed) how large a receive window it wants and how long it will take to process the data it does receive. The amount of time required to process data is called the Packet Processing Delay (PPD). The FEP then responds back to NTS with an Outgoing Call Reply message containing its receive window size and PPD to NTS.
The table below summarizes the Call Control sequence:
Notice that the FEP does not know its connect speed until just before the Outgoing Call Reply message on an outgoing call, and just bef ore the Incoming Call Connected message on an incoming call.
Figure 2 illustrates how calls can be aborted by the NTS:
Figure 2. How calls can be aborted by the NTS
Figure 3 illustrates how the FEP would abort/disconnect calls:
Figure 3. How FEP aborts/disconnects calls
The Outgoing Call Request is sent by the NTS when it needs to place an outgoing call. The NTS can request packet framing and call bearer capabilities as follows:
/* * Packet framing type: * * PPTP_ASYNC_FRAMING: Async PPP. * * PPTP_SYNC_FRAMING: Sync PPP. */ typedef enum { PPTP_ASYNC_FRAMING = 1, PPTP_SYNC_FRAMING, PPTP_DONT_CARE_FRAMING } PptpFramingType; /* * Call bearer type. * * PPTP_ANALOG_CALL: This is an analog call. * * PPTP_DIGITAL_CALL: This is a digital call. * * PPTP_DONT_CARE_BEARER_TYPE: Don't care what you use. */ typedef enum { PPTP_ANALOG_CALL = 1, PPTP_DIGITAL_CALL, PPTP_DONT_CARE_BEARER_TYPE } PptpCallBearerType;
The packet format for the Outgoing Call Request is as follows:
/* * The CallRequest Packet. * * This command can be sent from the NT to the FEP to * indicate a request for an outgoing call. * * callID: A unique identifier assigned by * the transmitter of this message. * This field can be used for routing * purposes. * * callSerialNumber A unique identifier assigned by * the transmitter of this message. * This field can be used to log events * associated with the call. * * minBPS: The lowest acceptable BPS for * outgoing calls. * * maxBPS: The highest acceptable BPS for * outgoing calls. * * bearerType: Call bearer type. One of * PptpCallBearerType. * * framingType: Link framing type. One of * PptpFramingType. * * packetWindow: The receive packet window of * the *sender* of this message. * * packetProcDelay: The packet processing delay * time for the sender of this packet. * This value is in 10ths of a second. * For example, 64 would mean 6.4 * seconds. * * phoneNumberLength: The actual number of valid digits * in the phoneNumber field. * * phoneNumber: The number that is to be dialed. * For ISDN and analog calls, the * phone number must be in ASCII. * * subAddress: Sub-address field. * */ typedef struct { Word callID; Word callSerialNumber; LongWord minBPS; LongWord maxBPS; LongWord bearerType; LongWord framingType; Word packetWindow; Word packetProcDelay; Word reserved; Word phoneNumberLength; Word reserved; Byte phoneNumber[ 64 ]; Byte subAddress[ 64 ]; } PptpOutCallRequest;
Because each endpoint involved in the call is allowed to assign its own callID, different routing schemes (for example, hashing, table lookup, binary search) can be used by each endpoint.
The Outgoing Call Reply is sent by the FEP to the NTS as a result of an Outgoing Call Request. This command may be sent back very quickly if no resources were available, or might be delayed until the call is placed.
The result of the call is returned in this packet and can have one of the following values:
/* * Result Codes for Call Replies. * * PPTP_OUTCALL_CONNECT: Call connected successfully. * * PPTP_OUTCALL_GENERAL_ERROR: See the generalErrorCode field. * * PPTP_OUTCALL_NO_CARRIER: No carrier. * * PPTP_OUTCALL_BUSY: Got a busy signal. * * PPTP_OUTCALL_NO_DIAL_TONE: No dial tone. * * PPTP_OUTCALL_TIMEOUT: The call did not complete in time. * * PPTP_OUTCALL_DONT_ACCEPT: Do not accept this call. */ typedef enum { PPTP_OUTCALL_CONNECT = 1, PPTP_OUTCALL_GENERAL_ERROR = 2, PPTP_OUTCALL_NO_CARRIER, PPTP_OUTCALL_BUSY, PPTP_OUTCALL_NO_DIAL_TONE, PPTP_OUTCALL_TIMEOUT, PPTP_OUTCALL_DONT_ACCEPT } PptpCallResultCode;
The packet format is as follows:
/* * The CallReply packet. * * callID: A unique identifier assigned by * FEP. This field can be used * for routing purposes. * * peersCallID: Set to the value received in the * Outgoing Call Request Message. * * resultCode: One of PptpCallResultCode. * * generalErrorCode: Contains the general error if * resultCode is *_GENERAL_ERROR. * * causeCode: This is the cause code and can * vary from call type to call type. * For ISDN calls, it is the Q.931 * cause code. * * connectSpeed: The actual connected speed in BPS. * * packetWindow: The receive packet window of * the *sender* of this message. * * packetProcDelay: The packet processing delay * time for the sender of this packet. * This value is in 10ths of a second. * For example, 64 would mean 6.4 * seconds. * * physChannelID: This field is initialized by * FEP, in a vendor-specific manner, * which defines which physical channel * number was used for this outgoing * call. */ typedef struct { Word callID; Word peersCallID; Byte resultCode; Byte generalErrorCode; Word causeCode; LongWord connectSpeed; Word packetWindow; Word packetProcDelay; LongWord physChannelID; } PptpOutCallReply;
The space of callID may be as small as the maximum number of simultaneous calls supported by the vendor's equipment. This field may be used for routing purposes within the vendor's equipment. Because callID may in fact map to a physical port with some implementations, it is suggested that the space of callID be at least {0..2*maxChannel}. This allows callID numbers to be retired in a leisurely fashion and eliminates potential race conditions from call to call.
The callID number is used to associate the Call Reply message with the Call Request message for both incoming and outgoing calls. After receiving this message, each endpoint can use the callID to associate control messages with a particular call. Note that the callID will be the same for all subsequent messages (for example, Call Clear, WAN Error Notify) transmitted by the same endpoint, but different between endpoints of the same call. To clarify, an NTS may transmit a Call Request message with callID set to 9. The FEP may transmit a Call Reply message with callID set to 44 and peersCallID set to 9. The NTS can now associate callID 44 received from the FEP with this call because the peersCallID in the Call Reply message matches the callID it sent in the Call Request message. All further control frames associated with this call will contain the peersCallID of 9 if transmitted by the FEP, or the peersCallID of 44 if transmitted by the NT.
The Incoming Call Request is sent by the FEP to the NTS when a client calls the FEP. The format of this message is:
/* * The Incoming Call Request packet. Sent by the FEP to the * NTS. * * callID: A unique identifier assigned by * the FEP. This field can be used * for routing purposes. * * callSerialNumber A unique identifier assigned by * the FEP. This field can be used * to log events associated with * the call. * * callBearerType: One of PptpCallBearerType. * * * physChannelID: This field is initialized by * FEP, in a vendor-specific manner, * which defines which physical channel * number was used for this incoming * call. * * dialedNumberLength: The actual number of valid digits * in the dialedNumber field. * * dialingNumberLength: The actual number of valid digits * in the dialingNumber field. * * dialedNumber: The number that was dialed by the * caller. * * dialingNumber: The phone number of the client. * * subAddress: The sub-address number. * */ typedef struct Word callID; Word callSerialNumber; LongWord callBearerType; LongWord physChannelID; Word dialedNumberLength; Word dialingNumberLength; Byte dialedNumber[ 64 ]; Byte dialingNumber[ 64 ]; Byte subAddress[ 64 ]; } PptpInCallRequest;
This message is sent by the NTS to the FEP as a reply to the Incoming Call Request sent by the FEP. The resultCode field in the Incoming Call Reply message can be one of the following:
/* * Possible result code sent by the NTS to the FEP in the * Incoming Call Reply. * * PPTP_INCALL_ACCEPT: Go ahead and answer the call. * * PPTP_INCALL_GENERAL_ERROR: See the generalErrorCode field. * * PPTP_INCALL_DONT_ACCEPT: Don't accept the call. Hang up. * */ typedef enum { PPTP_INCALL_ACCEPT = 1, PPTP_INCALL_GENERAL_ERROR = 2, PPTP_INCALL_DONT_ACCEPT, } PptpInCallResultCode; /* * The Incoming Call Reply packet. * * callID: A unique identifier assigned by * the NTS. This field can be used * for routing purposes. * * peersCallID: Set to the value received in the * callID field in the Incoming Call * Request. * * resultCode: One of PptpCallResultCode. * * generalErrorCode: Contains the general error code if * resultCode is *_GENERAL_ERROR. * * packetWindow: The receive packet window of * the *sender* of this message. * * packetProcDelay: The packet processing delay * time for the sender of this message. * This value is in 10ths of a second. * For example, 64 would mean 6.4 * seconds. */ typedef struct Word callID; Word peersCallID; Byte resultCode; Byte generalErrorCode; Word packetWindow; Word packetProcDelay; Word reserved; } PptpInCallRequest;
3.3.2.5 Incoming Call Connected
The Incoming Call Connected command is the last and final part of the three-way handshake used for incoming call establishment. Once this message is received, user data can be sent.
The format of this command is:
/* * The IncomingCallConnected packet. * * callID: Set to the value that was in the * callID field in the Incoming Call * Request message. * * peersCallID: Set to the value that was in the * callID field in the Incoming Call * Reply message. * * connectSpeed: Actual connected speed, in BPS. * * packetWindow: The receive packet window of * the *sender* of this message. * * packetProcDelay: The packet processing delay * time for the sender of this message. * This value is in 10ths of a second. * For example, 64 would mean 6.4 * seconds. * * framingType: The type of framing. * */ typedef struct { Word callID; Word peersCallID; LongWord connectSpeed; Word packetWindow; Word packetProcDelay LongWord callFramingType; } PptpInCallConnected;
This command is sent from the NTS to the FEP to clear a call. The call could be in any state other than Idle. The FEP will respond with a Call Disconnect Notify.
The format for this command is:
/* * PptpCallClearRequest * * This message is sent by a server to request a disconnect of a * connected call. * * callID: Call ID value used by originator. * This field is used instead of the * peer call ID because the value * of the peer call ID may not * be known during call establishment * * reserved: Currently used for padding * */ typedef struct { Word callID; Word reserved; } PptpClearCallRequest;
3.3.2.7 Call Disconnected Notify
This message is sent by the FEP to the NTS when the NTS requests that a call be cleared via the Clear Call Request message or when a call is dropped or disconnected.
Possible reasons for the call being cleared can be:
/* * Call Disconnect Notify Codes. * */ typedef enum { PPTP_DISCONNECT_LOST_CARRIER = 1, PPTP_DISCONNECT_GENERAL_ERROR = 2, PPTP_DISCONNECT_ADMIN_SHUTDOWN, } PptpCallDisconnectCode;
The format for this command is:
/* * The CallDisconnectNotify message. There is no reply to this * message. * * callID: Call ID value used by originator. * This field is used instead of the * peer call ID because the value * of the peer call ID may not * be known during call establishment. * * resultCode: One of CallDisconnectCode. * * generalErrorCode: A PptpGeneralErrorCode if resultCode is * PPTP_DISCONNECT_GENERAL_ERROR. * * causeCode: This is the cause code and can * vary from call type to call type. * For ISDN calls, it is the Q.931 * cause code. * * callStatistics: Vendor-specific call statistics * that can be log for diagnostic * purposes. Must be a null-terminated * ASCII string. */ typedef struct { Word callID; Byte resultCode; Byte generalErrorCode; Word causeCode; Word reserved; Byte callStatistics[ 128 ]; } PptpCallDisconnectNotify;
The Wan Error Notify message is sent by the FEP to the NTS to indicate WAN error conditions. The counters in this message are cumulative. This message should only be sent when an error occurs, and not more than once every 60 seconds. These counters must be reset when a call is established.
The format for this command is:
/* * The WanErrorNotify message. There is no reply to this * message. * * peersCallID: The peer's call indentifier. */ typedef struct { Word peersCallID; Word reserved; LongWord crcErrors; LongWord framingErrors; LongWord hardwareOverRuns; LongWord bufferOverRuns; LongWord timeoutErrors; LongWord alignmentErrors; } PptpWanErrorNotify;
The Set Link Info message is sent by the NTS to the FEP to set some of the PPP-negotiated options. These options can change at any time during the life of the call, so the FEP must be able to update its internal info dynamically.
The message format is:
/* * PptpSetLinkInfo * * This message is sent by the server to set the PPP LCP * options processing that should be done by a client, so * it can be offloaded from the server to obtain better * PPP throughput * * peerCallID Call ID value used by peer. * This field is filled in with * the callID field from the * message sent by the peer * during call establishment. * * reserved Currently used for padding. * * sendAccm Send ACCM value the client should use * to process outgoing PPP packets. * The default value used by the client until * this message is received is 0XFFFFFFFF. * * recvAccm: Recv ACCM value the client should use * to process incoming PPP packets. * The default value * used by the client until this * message is received is 0XFFFFFFFF. */ typedef struct { Word peerCallID; Word reserved; LongWord sendAccm; LongWord recvAccm; } PptpSetLinkInfo;
For more information on the specifics of these PPP options, please refer for RFC 1661, The Point-to-Point Protocol (PPP).
Once a call is established, user data messages can be sent to carry the user data. This document details enhancements to the Generic Routing Encapsulation (GRE) protocol (RFC 1701 and 1702) for use in transporting PPTP packets. The enhanced GRE packet format (GRE v2) is depicted in Figure 4.
+-----------------------------------------+ | Media Header | +-----------------------------------------+ | IP Header | +-----------------------------------------+ | GRE Header | +-----------------------------------------+ | Payload Packet | +-----------------------------------------+
Figure 4. GRE v2 packet
The GRE v2 header has the following format when used as the PPTP data stream protocol:
1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |C|R|K|S|s|Recur|A| Flags | Ver | Protocol Type | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Key (HW)Payload Length | Key (LW)Call ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number (Opt) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledge Number (Opt) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 5. GRE v2 packet
where:
The proposed solution requires a GRE v2 header and can be used directly over IP. Since the header size is variable in length, it makes the protocol overhead efficient. The maximum total header size at any given time would be 36 (20 IP + 16 GREv2 = 36), if both data and acknowledgements were carried within the packet.
This protocol allows acknowledgments to be carried with the data and makes the overall protocol more efficient, which in turn requires less buffering of packets.
This solution is backward-compatible with GRE v1.
The sliding window protocol used on the PPTP data path is used to flow control each side of the data exchange. The proposed GRE v2 IP protocol allows packet acknowledgments to be piggybacked on data packets. Acknowledgments can still be sent separately from data packets. The main purpose of the sliding window protocol is for flow control; retransmissions are not supported.
3.4.2.1 Multi Packet Acknowledgment
One feature of the PPTP sliding window protocol is to allow an acknowledgment of multiple packets with one acknowledgment. All outstanding packets with a lower or equal sequence number of the acknowledgment are cleared. Time-out calculations are performed using the highest number packet acknowledged, and all lower packet timers are cleared. This is shown in Figure 6 below.
Figure 6. Acknowledging multiple packets at once
In Figure 6, the FEP acknowledges the last packet it received. This effectively acknowledges all prior messages and allows NTS to send another window full. Adaptive time-out calculations are only performed when an Acknowledge is received. When multi-packet acknowledges are used, the overhead of the adaptive time-out algorithm is reduced. The FEP is not required to take advantage of the multi-packet acknowledge, and it can individually acknowledge each packet as they are sent to the PPP client.
3.4.2.2 Out-of-Order Packets
Occasionally packets lose their order across a complicated internetwork, as illustrated in Figure 7. Packet 3 arrives at the FEP after packet 4, although NTS sent them in order. The FEP acknowledges packet 4, and may assume packet 3 is lost. This acknowledgment grants credit beyond packet 4. When the FEP does receive packet 3, it should attempt to transmit it to the remote PPP client. When packet 5 comes in, it is acknowledged by the FEP since it has a higher sequence number than 4, which was the last highest packet acknowledged by the FEP.
Figure 7. Out-of-order packets
PPTP uses sliding windows and time-outs to provide both flow control across the internetwork as well as data buffering to keep the FEP data channel full. PPTP requires that a time-out be used to recover from dropped data or acknowledgment packets. The exact implementation of the time out is vendor-specific. It is suggested that an adaptive time-out be implemented with backoff for congestion control. The time-out mechanism proposed here has the following properties:
The PPD parameter is a 16-bit word exchanged during the Call Control phase that represents tenths of a second (64 means 6.4 seconds). The protocol only specifies that the parameter is exchanged; it does not specify how it is calculated. The way values for PPD are calculated is implementation-dependent and need not be variable (static time-outs). The PPD must be exchanged in the call connect sequence even if it remains constant in an implementation. One possible way to calculate the PPD is:
The header size consists of the IP and GRE v2 header, and is 36. The MTU is the overall MTU for the internetwork link between the FEP and NTS. WindowSize represents the number of packets in the sliding window, and its calculation or size is implementation-dependent. The amount of latency in the internetwork could be used to pick a window size sufficient to keep the current connection's pipe full. The constant 8 converts bytes to bits (assuming ConnectRate is in bits per second). If ConnectRate is in bytes per second, omit the 8. FEPFudge is not required but can be used to take overall processing overhead of the FEP into account.
The PPD is used to seed the adaptive algorithm with the initial RTT(n-1) value.
Sample is the actual measured time for a returned acknowledgment.
The RTT value represents our estimate of when the average packet acknowledge will return.
We still must decide how much time to allow for acknowledgments to return. If we set the time-out too high, we end up unnecessarily waiting for dropped packets. If the time-out is too short, we end up timing out just before the acknowledgment arrives. The acknowledgment time-out should also be reasonable and responsive to changing network congestion.
The suggested adap tive algorithm detailed below is based on the TCP 1989 implementation and is explained in Richard Steven's book TCP/IP Illustrated, Volume 1 (page 300). 'n' means this iteration of the calculation, and 'n-1' refers to values from the last calculation.
The final calculation of ATO should use a MIN function to ensure that the time-out does not exceed the maximum time-out.
If a time-out occurs, the time-out value should be adjusted upward. Although GRE v2 packets are not retransmitted when a time-out occurs, the time-out should be adjusted up toward a maximum limit. To compensate for shifting internetwork time delays, a strategy must be employed to increase the time-out when it expires. A simple formula called Karn's Algorithm is used in TCP implementations and may be used in implementing the backoff timers for NTS or the FEP. Notice that in addition to increasing the time-out, we are also shrinking the size of the window.
The timer backoff algorithm, as used in TCP, is:
Adapted to our time-out calculations, the new ATO is calculated:
In this modified calculation of ATO, only the two values that contribute to ATO and that are carried on to the next iteration are calculated. RTT is scaled by , and DEV is unmodified. DIFF is not carried forward and is not used in this scenario. is suggested to be 2.
Although each side has indicated the size of its receive window, it is recommended that a slow st art method be used to begin transmitting data. The initial window size on the transmitter is set to half the maximum size the receiver requested, with a minimum size of one packet. As the receiver successfully digests each window, the window size on the transmitter is bumped up by one packet to the initial maximum. This method prevents a system from flooding an already congested network because no history has been established.
When a time-out does occur on a packet, the sender adjusts the size of the transmit window down to one half its value when it failed. Fractions are rounded up, and the minimum window size is one.
With every successful transmission of a window without a time-out, the window is opened by one packet until it reaches the maximum window size that was sent by the other side when the call was connected. As stated earlier, no retransmission is done on a time-out. After a time-out, the transmission resumes with the window starting at one and adjusting upward with each success.
When a receiver's window overflows with too many incoming packets, excess packets are thrown away.
Figure 8. PPTP state diagrams