[kea-dev] Strange behaviour with Option 53 - what am I missing?

Dave Cole davecole at nbnco.com.au
Tue Jan 30 04:00:12 UTC 2018


After looking into this more deeply I suspect the only way I can provide a sensible wrapper for the Option class is to do a bunch of dynamic_pointer_cast operations in my Python wrapper constructor and save a type code.  Then I can just perform a switch in each method that has the potential to fail when I have a reference to an Option sub-class rather than the generic Option super-class.


I am not sure it makes sense to ever expose the ability to construct an Option sub-class from Python.


In any event, I am not going to bother doing this until I have tests for all of the functionality currently implemented.  You can quite happily do anything you need with just the Option class and methods.  For example:


>>> from kea import *
>>> from ipaddress import IPv4Address
>>> name_servers = [IPv4Address(u'192.0.3.1'), IPv4Address(u'192.0.3.2')]
>>> packed_name_servers = ''.join([addr.packed for addr in name_servers])
>>> o = Option(DHO_DOMAIN_NAME_SERVERS).setString(packed_name_servers)
>>> o.getString()
'\xc0\x00\x03\x01\xc0\x00\x03\x02'

Hmm... On further investigation, I am going to have to do the funny dynamic cast thing - even though we will not use it here :-).

>>> from kea import *
>>> from pcapfile import savefile
>>>
>>> def make_packet(frame):
...     ll = frame.packet
...     ip = ll.payload
...     udp = ip.payload
...     p = Pkt4(udp.payload.decode('hex'))
...     p.setLocalAddr(ip.src)
...     p.setRemoteAddr(ip.dst)
...     p.setLocalPort(udp.src_port)
...     p.setRemotePort(udp.dst_port)
...     p.unpack()
...     return p
...
>>> pcap = savefile.load_savefile(open('dhcp-trace.pcap'), layers=3)
>>> p = make_packet(pcap.packets[1])
>>> print p.toText()
local_address=172.20.0.4:67, remote_address=11.0.0.1:67, msg_type=DHCPOFFER (2), transid=0x566fcf25,
options:
  type=001, len=004: 255.255.240.0 (ipv4-address)
  type=003, len=004: 11.0.0.1
  type=042, len=004: 10.10.3.10
  type=043, len=058: 01:38:68:74:74:70:73:***private stuff***
  type=051, len=004: 86400 (uint32)
  type=053, len=001: 2 (uint8)
  type=054, len=004: 172.20.0.4 (ipv4-address)
  type=058, len=004: 43200 (uint32)
  type=059, len=004: 75600 (uint32)
  type=082, len=050:,
options:
    type=001, len=033: 53:57:***private stuff***
    type=002, len=013: 55:4e:***private stuff***
>>>
>>> o = p.getOption(3).getUint16()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Length (0) of buffer is insufficient to read a uint16_t

Oh well :-)

________________________________
From: kea-dev <kea-dev-bounces at lists.isc.org> on behalf of Dave Cole <davecole at nbnco.com.au>
Sent: Tuesday, 30 January 2018 11:45:12 AM
To: kea-dev at lists.isc.org
Subject: [kea-dev] Strange behaviour with Option 53 - what am I missing?

I have decided that before publishing my Python bindings I should add a bunch of unit tests.  In the process I discovered something strange:

>>> from kea import *
>>> p = Pkt4(DHCPDISCOVER, 0)
>>> o = p.getOptions()[DHO_DHCP_MESSAGE_TYPE]
>>> o.getUint8()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Attempt to read uint8 from option 53 that has size 0
>>> o.getString()
'\x01'

The implementation of getUint8 is:

static PyObject *
Option_getUint8(OptionObj *self, PyObject *args) {
    try {
        return (PyInt_FromLong(self->option_ptr->getUint8()));
    } catch (const exception &e) {
        return (raisePythonError(PyExc_TypeError, e.what()));
    }
}

Whereas the getString is:

static PyObject *
Option_getString(OptionObj *self, PyObject *args) {
    try {
        vector<uint8_t> value = self->option_ptr->toBinary();
        return (PyString_FromStringAndSize((const char *) &value[0], value.size()));
    } catch (const exception &e) {
        return (raisePythonError(PyExc_TypeError, e.what()));
    }
}

So when I step through the code with gdb it appears that DHO_DHCP_MESSAGE_TYPE is an instance of OptionInt which does not use the data_ member of Option, instead it has another member value_ of the type defined in the template argument.

Is it a deliberate choice in the code to require application code to just know that getUint8 does not work for OptionInt (which subclasses Option)?

Someone with more of a clue about c++ than me might be able to tell me an easy (and efficient) way I can make my code notice an OptionInt and internally use getValue() rather than getUint8().  Would a dynamic_pointer_cast<OptionUint8>(self->option_ptr) be the way to go?
_______________________________________________
kea-dev mailing list
kea-dev at lists.isc.org
https://lists.isc.org/mailman/listinfo/kea-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.isc.org/pipermail/kea-dev/attachments/20180130/8ef5d103/attachment-0001.html>


More information about the kea-dev mailing list