diff --git a/scapy/config.py b/scapy/config.py
index b97ee0bd0e9eb54c9c7e31d7ac04c57e943bf512..fe541ed5705be605d90ea517eead7de7ce2a070d 100755
--- a/scapy/config.py
+++ b/scapy/config.py
@@ -391,7 +391,7 @@ contribs: a dict which can be used by contrib layers to store local configuratio
     geoip_city_ipv6 = '/usr/share/GeoIP/GeoIPCityv6.dat'
     load_layers = ["l2", "inet", "dhcp", "dns", "dot11", "gprs", "tls",
                    "hsrp", "inet6", "ir", "isakmp", "l2tp", "mgcp",
-                   "mobileip", "netbios", "netflow", "ntp", "ppp",
+                   "mobileip", "netbios", "netflow", "ntp", "ppp", "pptp",
                    "radius", "rip", "rtp", "skinny", "smb", "snmp",
                    "tftp", "x509", "bluetooth", "dhcp6", "llmnr",
                    "sctp", "vrrp", "ipsec", "lltd", "vxlan"]
diff --git a/scapy/layers/pptp.py b/scapy/layers/pptp.py
new file mode 100644
index 0000000000000000000000000000000000000000..16a9c6d363f78fd68346070b8a327d2176d51c46
--- /dev/null
+++ b/scapy/layers/pptp.py
@@ -0,0 +1,373 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Jan Sebechlebsky <sebechlebskyjan@gmail.com>
+## This program is published under a GPLv2 license
+
+"""
+PPTP (Point to Point Tunneling Protocol)
+
+[RFC 2637]
+"""
+
+from scapy.packet import Packet, bind_layers
+from scapy.layers.inet import TCP
+from scapy.fields import ByteEnumField, FieldLenField, FlagsField, IntField, IntEnumField,\
+                         LenField, XIntField, ShortField, ShortEnumField, StrFixedLenField,\
+                         StrLenField, XShortField, XByteField
+
+_PPTP_MAGIC_COOKIE = 0x1a2b3c4d
+
+_PPTP_msg_type = {1: "Control Message",
+                  2: "Managemenent Message"}
+
+_PPTP_ctrl_msg_type = {  # Control Connection Management
+                       1: "Start-Control-Connection-Request",
+                       2: "Start-Control-Connection-Reply",
+                       3: "Stop-Control-Connection-Request",
+                       4: "Stop-Control-Connection-Reply",
+                       5: "Echo-Request",
+                       6: "Echo-Reply",
+                       # Call Management
+                       7: "Outgoing-Call-Request",
+                       8: "Outgoing-Call-Reply",
+                       9: "Incoming-Call-Request",
+                       10: "Incoming-Call-Reply",
+                       11: "Incoming-Call-Connected",
+                       12: "Call-Clear-Request",
+                       13: "Call-Disconnect-Notify",
+                       # Error Reporting
+                       14: "WAN-Error-Notify",
+                       # PPP Session Control
+                       15: "Set-Link-Info"}
+
+_PPTP_general_error_code = {0: "None",
+                            1: "Not-Connected",
+                            2: "Bad-Format",
+                            3: "Bad-Value",
+                            4: "No-Resource",
+                            5: "Bad-Call ID",
+                            6: "PAC-Error"}
+
+
+class PPTP(Packet):
+    name = "PPTP"
+    fields_desc = [FieldLenField("len", None, fmt="H", length_of="data",
+                                 adjust=lambda p, x: x + 12),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 1, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   StrLenField("data", "",length_from=lambda p: p.len - 12)]
+
+    registered_options = {}
+
+    @classmethod
+    def register_variant(cls):
+        cls.registered_options[cls.ctrl_msg_type.default] = cls
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt:
+            o = ord(_pkt[9])
+            return cls.registered_options.get(o, cls)
+        return cls
+
+
+_PPTP_FRAMING_CAPABILITIES_FLAGS = ["Asynchronous Framing supported",
+                                    "Synchronous Framing supported"]
+
+_PPTP_BEARER_CAPABILITIES_FLAGS = ["Analog access supported",
+                                   "Digital access supported"]
+
+
+class PPTPStartControlConnectionRequest(PPTP):
+    name = "PPTP Start Control Connection Request"
+    fields_desc = [LenField("len", 156),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 1, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("protocol_version", 1),
+                   XShortField("reserved_1", 0x0000),
+                   FlagsField("framing_capabilities", 0, 32,
+                              _PPTP_FRAMING_CAPABILITIES_FLAGS),
+                   FlagsField("bearer_capabilities", 0, 32,
+                              _PPTP_BEARER_CAPABILITIES_FLAGS),
+                   ShortField("maximum_channels", 65535),
+                   ShortField("firmware_revision", 256),
+                   StrFixedLenField("host_name", "linux", 64),
+                   StrFixedLenField("vendor_string", "", 64)]
+
+_PPTP_start_control_connection_result = {1: "OK",
+                                         2: "General error",
+                                         3: "Command channel already exists",
+                                         4: "Not authorized",
+                                         5: "Unsupported protocol version"}
+
+
+class PPTPStartControlConnectionReply(PPTP):
+    name = "PPTP Start Control Connection Reply"
+    fields_desc = [LenField("len", 156),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 2, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("protocol_version", 1),
+                   ByteEnumField("result_code", 1,
+                                 _PPTP_start_control_connection_result),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   FlagsField("framing_capabilities", 0, 32,
+                              _PPTP_FRAMING_CAPABILITIES_FLAGS),
+                   FlagsField("bearer_capabilities", 0, 32,
+                              _PPTP_BEARER_CAPABILITIES_FLAGS),
+                   ShortField("maximum_channels", 65535),
+                   ShortField("firmware_revision", 256),
+                   StrFixedLenField("host_name", "linux", 64),
+                   StrFixedLenField("vendor_string", "", 64)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPStartControlConnectionRequest)
+
+
+_PPTP_stop_control_connection_reason = {1: "None",
+                                        2: "Stop-Protocol",
+                                        3: "Stop-Local-Shutdown"}
+
+
+class PPTPStopControlConnectionRequest(PPTP):
+    name = "PPTP Stop Control Connection Request"
+    fields_desc = [LenField("len", 16),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 3, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ByteEnumField("reason", 1,
+                                 _PPTP_stop_control_connection_reason),
+                   XByteField("reserved_1", 0x00),
+                   XShortField("reserved_2", 0x0000)]
+
+_PPTP_stop_control_connection_result = {1: "OK",
+                                        2: "General error"}
+
+
+class PPTPStopControlConnectionReply(PPTP):
+    name = "PPTP Stop Control Connection Reply"
+    fields_desc = [LenField("len", 16),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 4, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ByteEnumField("result_code", 1,
+                                 _PPTP_stop_control_connection_result),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   XShortField("reserved_2", 0x0000)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPStopControlConnectionRequest)
+
+
+class PPTPEchoRequest(PPTP):
+    name = "PPTP Echo Request"
+    fields_desc = [LenField("len", 16),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 5, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   IntField("identifier", None)]
+
+_PPTP_echo_result = {1: "OK",
+                     2: "General error"}
+
+
+class PPTPEchoReply(PPTP):
+    name = "PPTP Echo Reply"
+    fields_desc = [LenField("len", 20),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 6, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   IntField("identifier", None),
+                   ByteEnumField("result_code", 1, _PPTP_echo_result),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   XShortField("reserved_1", 0x0000)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPEchoRequest) and other.identifier == self.identifier
+
+_PPTP_bearer_type = {1: "Analog channel",
+                     2: "Digital channel",
+                     3: "Any type of channel"}
+
+_PPTP_framing_type = {1: "Asynchronous framing",
+                      2: "Synchronous framing",
+                      3: "Any type of framing"}
+
+
+class PPTPOutgoingCallRequest(PPTP):
+    name = "PPTP Outgoing Call Request"
+    fields_desc = [LenField("len", 168),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 7, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   ShortField("call_serial_number", 0),
+                   IntField("minimum_bps", 32768),
+                   IntField("maximum_bps", 2147483648),
+                   IntEnumField("bearer_type", 3, _PPTP_bearer_type),
+                   IntEnumField("framing_type", 3, _PPTP_framing_type),
+                   ShortField("pkt_window_size", 16),
+                   ShortField("pkt_proc_delay", 0),
+                   ShortField('phone_number_len', 0),
+                   XShortField("reserved_1", 0x0000),
+                   StrFixedLenField("phone_number", '', 64),
+                   StrFixedLenField("subaddress", '', 64)]
+
+_PPTP_result_code = {1: "Connected",
+                     2: "General error",
+                     3: "No Carrier",
+                     4: "Busy",
+                     5: "No dial tone",
+                     6: "Time-out",
+                     7: "Do not accept"}
+
+
+class PPTPOutgoingCallReply(PPTP):
+    name = "PPTP Outgoing Call Reply"
+    fields_desc = [LenField("len", 32),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 8, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   ShortField("peer_call_id", 1),
+                   ByteEnumField("result_code", 1, _PPTP_result_code),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   ShortField("cause_code", 0),
+                   IntField("connect_speed", 100000000),
+                   ShortField("pkt_window_size", 16),
+                   ShortField("pkt_proc_delay", 0),
+                   IntField("channel_id", 0)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPOutgoingCallRequest) and other.call_id == self.peer_call_id
+
+
+class PPTPIncomingCallRequest(PPTP):
+    name = "PPTP Incoming Call Request"
+    fields_desc = [LenField("len", 220),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 9, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   ShortField("call_serial_number", 1),
+                   IntEnumField("bearer_type", 3, _PPTP_bearer_type),
+                   IntField("channel_id", 0),
+                   ShortField("dialed_number_len", 0),
+                   ShortField("dialing_number_len", 0),
+                   StrFixedLenField("dialed_number", "", 64),
+                   StrFixedLenField("dialing_number", "", 64),
+                   StrFixedLenField("subaddress", "", 64)]
+
+
+class PPTPIncomingCallReply(PPTP):
+    name = "PPTP Incoming Call Reply"
+    fields_desc = [LenField("len", 148),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 10, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   ShortField("peer_call_id", 1),
+                   ByteEnumField("result_code", 1, _PPTP_result_code),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   ShortField("pkt_window_size", 64),
+                   ShortField("pkt_transmit_delay", 0),
+                   XShortField("reserved_1", 0x0000)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPIncomingCallRequest) and other.call_id == self.peer_call_id
+
+
+class PPTPIncomingCallConnected(PPTP):
+    name = "PPTP Incoming Call Connected"
+    fields_desc = [LenField("len", 28),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 11, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("peer_call_id", 1),
+                   XShortField("reserved_1", 0x0000),
+                   IntField("connect_speed", 100000000),
+                   ShortField("pkt_window_size", 64),
+                   ShortField("pkt_transmit_delay", 0),
+                   IntEnumField("framing_type", 1, _PPTP_framing_type)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPIncomingCallReply) and other.call_id == self.peer_call_id
+
+
+class PPTPCallClearRequest(PPTP):
+    name = "PPTP Call Clear Request"
+    fields_desc = [LenField("len", 16),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 12, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   XShortField("reserved_1", 0x0000)]
+
+_PPTP_call_disconnect_result = {1: "Lost Carrier",
+                                2: "General error",
+                                3: "Admin Shutdown",
+                                4: "Request"}
+
+
+class PPTPCallDisconnectNotify(PPTP):
+    name = "PPTP Call Disconnect Notify"
+    fields_desc = [LenField("len", 148),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 13, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   ByteEnumField("result_code", 1,
+                                 _PPTP_call_disconnect_result),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   ShortField("cause_code", 0),
+                   XShortField("reserved_1", 0x0000),
+                   StrFixedLenField("call_statistic", "", 128)]
+
+
+class PPTPWANErrorNotify(PPTP):
+    name = "PPTP WAN Error Notify"
+    fields_desc = [LenField("len", 40),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 14, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("peer_call_id", 1),
+                   XShortField("reserved_1", 0x0000),
+                   IntField("crc_errors", 0),
+                   IntField("framing_errors", 0),
+                   IntField("hardware_overruns", 0),
+                   IntField("buffer_overruns", 0),
+                   IntField("time_out_errors", 0),
+                   IntField("alignment_errors", 0)]
+
+
+class PPTPSetLinkInfo(PPTP):
+    name = "PPTP Set Link Info"
+    fields_desc = [LenField("len", 24),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 15, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("peer_call_id", 1),
+                   XShortField("reserved_1", 0x0000),
+                   XIntField("send_accm", 0x00000000),
+                   XIntField("receive_accm", 0x00000000)]
+
+bind_layers(TCP, PPTP, sport=1723)
+bind_layers(TCP, PPTP, dport=1723)