blob: 0840026b4de0d1755cc37d717abffa7a85042ffd [file] [log] [blame]
# $Id: ppp.py 65 2010-03-26 02:53:51Z dugsong $
# -*- coding: utf-8 -*-
"""Point-to-Point Protocol."""
from __future__ import absolute_import
import struct
from . import dpkt
# XXX - finish later
# http://www.iana.org/assignments/ppp-numbers
PPP_IP = 0x21 # Internet Protocol
PPP_IP6 = 0x57 # Internet Protocol v6
# Protocol field compression
PFC_BIT = 0x01
class PPP(dpkt.Packet):
# Note: This class is subclassed in PPPoE
"""Point-to-Point Protocol.
TODO: Longer class information....
Attributes:
__hdr__: Header fields of PPP.
TODO.
"""
__hdr__ = (
('addr', 'B', 0xff),
('cntrl', 'B', 3),
('p', 'B', PPP_IP),
)
_protosw = {}
@classmethod
def set_p(cls, p, pktclass):
cls._protosw[p] = pktclass
@classmethod
def get_p(cls, p):
return cls._protosw[p]
def unpack(self, buf):
dpkt.Packet.unpack(self, buf)
if self.p & PFC_BIT == 0:
try:
self.p = struct.unpack('>H', buf[2:4])[0]
except struct.error:
raise dpkt.NeedData
self.data = self.data[1:]
try:
self.data = self._protosw[self.p](self.data)
setattr(self, self.data.__class__.__name__.lower(), self.data)
except (KeyError, struct.error, dpkt.UnpackError):
pass
def pack_hdr(self):
try:
if self.p > 0xff:
return struct.pack('>BBH', self.addr, self.cntrl, self.p)
return dpkt.Packet.pack_hdr(self)
except struct.error as e:
raise dpkt.PackError(str(e))
def __load_protos():
g = globals()
for k, v in g.items():
if k.startswith('PPP_'):
name = k[4:]
modname = name.lower()
try:
mod = __import__(modname, g, level=1)
PPP.set_p(v, getattr(mod, name))
except (ImportError, AttributeError):
continue
def _mod_init():
"""Post-initialization called when all dpkt modules are fully loaded"""
if not PPP._protosw:
__load_protos()
def test_ppp():
# Test protocol compression
s = b"\xff\x03\x21"
p = PPP(s)
assert p.p == 0x21
s = b"\xff\x03\x00\x21"
p = PPP(s)
assert p.p == 0x21
def test_ppp_short():
s = b"\xff\x03\x00"
import pytest
pytest.raises(dpkt.NeedData, PPP, s)
def test_packing():
p = PPP()
assert p.pack_hdr() == b"\xff\x03\x21"
p.p = 0xc021 # LCP
assert p.pack_hdr() == b"\xff\x03\xc0\x21"
def test_ppp_classmethods():
import pytest
class TestProto(dpkt.Packet):
pass
proto_number = 123
# asserting that this proto is not currently added
with pytest.raises(KeyError):
PPP.get_p(proto_number)
PPP.set_p(proto_number, TestProto)
assert PPP.get_p(proto_number) == TestProto
# we need to reset the class, or impact other tests
del PPP._protosw[proto_number]
def test_unpacking_exceptions():
from dpkt import ip
from binascii import unhexlify
buf_ppp = unhexlify(
'ff' # addr
'03' # cntrl
'21' # p (PPP_IP)
)
buf_ip = unhexlify(
'45' # _v_hl
'00' # tos
'0014' # len
'0000' # id
'0000' # off
'80' # ttl
'06' # p
'd47e' # sum
'11111111' # src
'22222222' # dst
)
buf = buf_ppp + buf_ip
ppp = PPP(buf)
assert hasattr(ppp, 'ip')
assert isinstance(ppp.data, ip.IP)
assert bytes(ppp) == buf
def test_ppp_packing_error():
import pytest
# addr is a 1-byte field, so this will overflow when packing
ppp = PPP(p=257, addr=1234)
with pytest.raises(dpkt.PackError):
ppp.pack_hdr()
def test_proto_loading():
# test that failure to load protocol handlers isn't catastrophic
standard_protos = PPP._protosw
# delete existing protos
PPP._protosw = {}
assert not PPP._protosw
# inject a new global variable to be picked up
globals()['PPP_NON_EXISTENT_PROTO'] = "FAIL"
_mod_init()
# we should get the same answer as if NON_EXISTENT_PROTO didn't exist
assert PPP._protosw == standard_protos