Example #1
0
class ARP(pypacker.Packet):
    __hdr__ = (
        ("hrd", "H", ARP_HRD_ETH),
        ("pro", "H", ARP_PRO_IP),
        ("hln", "B", 6),  # hardware address length
        ("pln", "B", 4),  # protocol address length
        ("op", "H", ARP_OP_REQUEST),
        ("sha", "6s", b"\x00" * 6),  # sender mac
        ("spa", "4s", b"\x00" * 4),  # sender ip
        ("tha", "6s", b"\x00" * 6),  # target mac
        ("tpa", "4s", b"\x00" * 4)  # target ip
    )

    # convenient access
    sha_s = pypacker.get_property_mac("sha")
    spa_s = pypacker.get_property_ip4("spa")
    tha_s = pypacker.get_property_mac("tha")
    tpa_s = pypacker.get_property_ip4("tpa")
Example #2
0
class IGMP(pypacker.Packet):
    __hdr__ = (("type", "B", 0), ("maxresp", "B", 0),
               ("sum", "H", 0, FIELD_FLAG_AUTOUPDATE), ("group", "4s",
                                                        b"\x00" * 4))

    # Convenient access for: group[_s]
    group_s = pypacker.get_property_ip4("group")

    def _update_fields(self):
        if self.sum_au_active and self._changed():
            self.sum = 0
            self.sum = checksum.in_cksum(pypacker.Packet.bin(self))
Example #3
0
class IGMP(pypacker.Packet):
    __hdr__ = (("type", "B", 0), ("maxresp", "B", 0), ("sum", "H", 0, True),
               ("group", "4s", b"\x00" * 4))

    # Convenient access for: group[_s]
    group_s = pypacker.get_property_ip4("group")

    def bin(self, update_auto_fields=True):
        if update_auto_fields and self.sum_au_active and self._changed():
            self.sum = 0
            self.sum = checksum.in_cksum(pypacker.Packet.bin(self))
        return pypacker.Packet.bin(self, update_auto_fields=update_auto_fields)
Example #4
0
class IP(pypacker.Packet):
    __hdr__ = (
        ("v_hl", "B", 69, FIELD_FLAG_AUTOUPDATE),  # = 0x45
        ("tos", "B", 0),
        ("len", "H", 20, FIELD_FLAG_AUTOUPDATE),
        ("id", "H", 0),
        # TODO: rename to frag_off
        ("off", "H", 0),
        ("ttl", "B", 64),
        ("p", "B", IP_PROTO_TCP,
         FIELD_FLAG_AUTOUPDATE | FIELD_FLAG_IS_TYPEFIELD),
        ("sum", "H", 0, FIELD_FLAG_AUTOUPDATE),
        ("src", "4s", b"\x00" * 4),
        ("dst", "4s", b"\x00" * 4),
        ("opts", None, triggerlist.TriggerList))

    __handler__ = {
        IP_PROTO_ICMP: icmp.ICMP,
        IP_PROTO_IGMP: igmp.IGMP,
        IP_PROTO_TCP: tcp.TCP,
        IP_PROTO_UDP: udp.UDP,
        IP_PROTO_IP6: ip6.IP6,
        IP_PROTO_ESP: esp.ESP,
        IP_PROTO_PIM: pim.PIM,
        IP_PROTO_IPXIP: ipx.IPX,
        IP_PROTO_SCTP: sctp.SCTP,
        IP_PROTO_OSPF: ospf.OSPF
    }

    def __get_v(self):
        return self.v_hl >> 4

    def __set_v(self, value):
        self.v_hl = (value << 4) | (self.v_hl & 0xf)

    v = property(__get_v, __set_v)

    def __get_hl(self):
        return self.v_hl & 0x0f

    def __set_hl(self, value):
        self.v_hl = (self.v_hl & 0xf0) | value

    hl = property(__get_hl, __set_hl)

    def __get_flags(self):
        return (self.off & 0xE000) >> 13

    def __set_flags(self, value):
        self.off = (self.off & ~0xE000) | (value << 13)

    flags = property(__get_flags, __set_flags)

    def __get_offset(self):
        return self.off & ~0xE000

    def __set_offset(self, value):
        self.off = (self.off & 0xE000) | value

    offset = property(__get_offset, __set_offset)

    def create_fragments(self, fragment_len=1480):
        """
		Create fragment packets from this IP packet with max fragment_len bytes each.
		This will set the flags and offset values accordingly (see header field off).

		fragment_len -- max length of a fragment (IP header + payload)
		return -- fragment IP packets created from this packet
		"""
        if fragment_len % 8 != 0:
            raise Exception("fragment_len not multipe of 8 bytes: %r" %
                            fragment_len)

        fragments = []
        length_ip_total = len(self.bin())
        payload = self.body_bytes
        length_ip_header = length_ip_total - len(payload)
        length_payload = length_ip_total - length_ip_header

        off = 0

        while off < length_payload:
            payload_sub = payload[off:off + fragment_len]

            ip_frag = IP(id=self.id, p=self.p, src=self.src, dst=self.dst)

            if length_payload - off > fragment_len:
                # more fragments follow
                ip_frag.flags = 0x1
            else:
                # last fragment
                ip_frag.flags = 0x0

            ip_frag.offset = int(off / 8)
            ip_frag.body_bytes = payload_sub
            fragments.append(ip_frag)
            off += fragment_len

        return fragments

    # Convenient access for: src[_s], dst[_s]
    src_s = pypacker.get_property_ip4("src")
    dst_s = pypacker.get_property_ip4("dst")

    def _dissect(self, buf):
        total_header_length = ((buf[0] & 0xf) << 2)
        options_length = total_header_length - 20  # total IHL - standard IP-len = options length

        if options_length < 0:
            # invalid header length: assume no options at all
            raise Exception("invalid header length: %d" % options_length)
        elif options_length > 0:
            # logger.debug("got some IP options: %s" % tl_opts)
            self._init_triggerlist("opts", buf[20:20 + options_length],
                                   self.__parse_opts)

        self._init_handler(buf[9], buf[total_header_length:])
        return total_header_length

    __IP_OPT_SINGLE = {IP_OPT_EOOL, IP_OPT_NOP}

    @staticmethod
    def __parse_opts(buf):
        """Parse IP options and return them as list."""
        optlist = []
        i = 0
        p = None

        while i < len(buf):
            # logger.debug("got IP-option type %s" % buf[i])
            if buf[i] in IP.__IP_OPT_SINGLE:
                p = IPOptSingle(type=buf[i])
                i += 1
            else:
                olen = buf[i + 1]
                # logger.debug("IPOptMulti")
                p = IPOptMulti(type=buf[i],
                               len=olen,
                               body_bytes=buf[i + 2:i + olen])
                # logger.debug("body bytes: %s" % buf[i + 2: i + olen])
                i += olen  # typefield + lenfield + data-len
                # logger.debug("IPOptMulti 2")
            optlist.append(p)
        return optlist

    def bin(self, update_auto_fields=True):
        if update_auto_fields and self._changed():
            self._update_bodyhandler_id()

            if self.len_au_active:
                self.len = len(self)
            if self.v_hl_au_active:
                # Update header length. NOTE: needs to be a multiple of 4 Bytes.
                # logger.debug("updating: %r" % self._packet)
                # options length need to be multiple of 4 Bytes
                self.hl = int(self.header_len / 4) & 0xf
            if self.sum_au_active:
                # length changed so we have to recalculate checksum
                # logger.debug(">>> IP: calculating sum")
                # reset checksum for recalculation,  mark as changed / clear cache
                self.sum = 0
                # logger.debug(">>> IP: bytes for sum: %s" % self.header_bytes)
                self.sum = in_cksum(self._pack_header())
                # logger.debug("IP: new hl: %d / %d" % (self._packet.hdr_len, hdr_len_off))
                # logger.debug("new sum: %0X" % self.sum)

        return pypacker.Packet.bin(self, update_auto_fields=update_auto_fields)

    def direction(self, other):
        # logger.debug("checking direction: %s<->%s" % (self, next))
        # TODO: handle broadcast
        if self.src == other.src and self.dst == other.dst:
            # consider packet to itself: can be DIR_REV
            return pypacker.Packet.DIR_SAME | pypacker.Packet.DIR_REV
        elif self.src == other.dst and self.dst == other.src:
            return pypacker.Packet.DIR_REV
        else:
            return pypacker.Packet.DIR_UNKNOWN

    def reverse_address(self):
        self.src, self.dst = self.dst, self.src
Example #5
0
class NewProtocol(pypacker.Packet):
    """
	New protocols are subclassing Packet class and represent a layer in a multi-layer
	network Packet like 'NewProtocol | IP | TCP ...'.
	The whole structure is oriented	at the ISO/OSI protocol layers where
	every layer contains a reference to the next upper layer. As an example this layer
	'NewProtocol', when parsing from raw bytes, will have a reference to the next upper
	layer 'IP' which can be access via '.' like 'newprotoinstance.ip' (access name is the
	lower case name of the class). Even higher layers can be accessed via
	'newprotoinstance.ip.tcp' (when available) or via the '[]' notation like 'newprotoinstance[TCP]'."""
    """
	The protocol header is basically defined by the static field
	"__hdr__" (see layer12/ethernet.Ethernet). See code documentation
	for classes "MetaPacket" and "Packet" in pypacker/pypacker.py for
	deeper information.
	"""
    __hdr__ = (
        # Simple constant fields: fixed format, not changing length
        # marked as type field: defines type of next upper layer, here: IP. See __handler__
        ("type", "B", TYPE_VALUE_IP, FIELD_FLAG_IS_TYPEFIELD),
        ("src", "4s", b"\xff" * 4),
        ("dst", "4s", b"\xff" * 4),
        # Simple constant field, marked for auto update, see _update_fields(...).
        #  Stores the full header length inclusive options.
        ("hlen", "H", 14, FIELD_FLAG_AUTOUPDATE),
        # Simple constant field, deactivated (see Ethernet -> vlan)
        # Switching between active/inactive should be avoided because of performance penalty :/
        ("idk", "H", None),
        # Again a simple constant field
        ("flags", "B", 0),
        # Dynamic field: bytestring format, *can* change in length, see dns.DNS
        # Field type should be avoided because of performance penalty :/
        ("yolo", None, b"1234"),
        # TriggerList field: variable length, can contain: raw bytes, key/value-tuples (see HTTP) and Packets (see IP)
        # Here TriggerList will contain key/value Tuples like (b"\x00", b"1")
        ("options", None, triggerlist.TriggerList))

    # Conveniant access should be enabled using properties eg using pypacker.get_property_xxx(...)
    src_s = pypacker.get_property_ip4("src")
    dst_s = pypacker.get_property_ip4("dst")

    # xxx_s = pypacker.get_property_mac("xxx")
    # xxx_s = pypacker.get_property_dnsname("xxx")

    # Setting/getting values smaller then 1 Byte should be enabled using properties (see layer3/ip.IP -> v, hl)
    def __get_flag_fluxcapacitor(self):
        return (self.flags & 0x80) >> 15

    def __set_flag_fluxcapacitor(self, value):
        value_shift = (value & 1) << 15
        self.flags = (self.flags & ~0x80) | value_shift

    flag_fluxcapacitor = property(__get_flag_fluxcapacitor,
                                  __set_flag_fluxcapacitor)

    @staticmethod
    def _parse_options(buf):
        """
		Callback to parse contents for TriggerList-field options,
		see _dissec(...) -> _init_triggerlist(...).
		return -- [Option(), ...]
		"""
        ret = []
        off = 0

        while off < len(buf):
            ret.append(Option(buf[off:off + 2]))
            off += 2
        return ret

    def _dissect(self, buf):
        """
		_dissect(...) must be overwritten if the header format can change
		from its original format. This is generally the case when
		- using TriggerLists (see ip.IP)
		- simple fields can get deactivated (see ethernet.Ethernet)
		- using dynamic fields (see dns.DNS)

		In NewProtocol idk can get deactivated, options is a TriggerList
		and yolo is a dynamic field so _dissect(...) needs to be defined.
		"""
        # Header fields are not yet accessible in _dissect(...) so basic information
        # (type info, header length, bytes of dynamic content etc) has to be parsed manually.
        upper_layer_type = buf[
            0]  # extract type information of next layer, here it can only be 0x66 but we extract it anyway
        # logger.debug("Found type: 0x%X" % upper_layer_type)
        total_header_length = unpack_H(buf[9:11])[0]
        yolo_len = 4 if upper_layer_type == TYPE_VALUE_IP else 5  # length of yolo is derived from type
        # logger.debug("Found length: %d, yolo=%d" % (total_header_length, yolo_len))
        tl_bts = buf[
            12 + yolo_len:
            total_header_length]  # options are the the end of the header
        # logger.debug("Bytes for TriggerList: %r" % tl_bts)
        # self._init_triggerlist(...) should be called to initiate TriggerLists,
        # otherwise the list will be empty. _parse_options(...) is a callback returning a list
        # of [raw bytes | key/value tuples | Packets] parsed from tl_bts.
        self._init_triggerlist("options", tl_bts, NewProtocol._parse_options)

        # self._init_handler(...) must be called to initiate the handler of the next
        # upper layer and makes it accessible (eg "ip" in "ethernet" via "ethernet.ip" or ethernet[ip.IP]).
        # Which handler to be initialized generally depends on the type information (here upper_layer_type)
        # found in the current layer (see layer12/ethernet.Ethernet -> type).
        # Here upper_layer_type can become the value 0x66 (defined by __handler__ field) and
        # as a result ip.IP will be created as upper layer using the bytes given by "buf[total_header_length:]".
        self._init_handler(upper_layer_type, buf[total_header_length:])
        return total_header_length

    """
	Handler can be registered by defining the static dictionary
	__handler__ where the key is extracted from raw bytes in _dissect(...) and
	given to _init_handler(...) and the value is the Packet class used to
	create the next upper layer (here ip.IP). Add the "FIELD_FLAG_IS_TYPEFIELD"
	to the corresponding type field in __hdr__.
	"""
    __handler__ = {TYPE_VALUE_IP: ip.IP}  # just 1 possible upper layer

    def _update_fields(self):
        """
		_update_fields(...) should be overwritten to update fields which depend on the state
		of the packet like lengths, checksums etc (see layer3/ip.IP -> len, sum)
		aka auto-update fields.	The variable XXX_au_active indicates
		if the field XXX should be updated (True) or not
		(see layer3/ip.IP.bin() -> len_au_active). XXX_au_active is
		available if the field has the flag "FIELD_FLAG_AUTOUPDATE" in __hdr__,
		default value is True. _update_fields(...) is implicitly called by bin(...).
		"""
        if self._changed() and self.hlen_au_active:
            self.hlen = self.header_len

    def bin(self, update_auto_fields=True):
        """
		bin(...)  should only be overwritten to allow more complex assemblation eg adding padding
		at the end of all layers instead of the current layer (see ethernet.Ethernet -> padding).
		The variable update_auto_fields indicates if fields should be updated in general.
		"""
        return pypacker.Packet.bin(
            self, update_auto_fields=update_auto_fields) + b"somepadding"

    def direction(self, other):
        """
		direction(...) should be overwritten to be able to check directions to an other packet
		(see layer12/ethernet.Ethernet)
		"""
        direction = 0

        if self.src == other.src and self.dst == other.dst:
            direction |= pypacker.Packet.DIR_SAME
        if self.src == other.dst and self.dst == other.src:
            direction |= pypacker.Packet.DIR_REV

        if direction == 0:
            direction = pypacker.Packet.DIR_UNKNOWN
        return direction

    def reverse_address(self):
        """
		reverse_address(...) should be overwritten to be able to reverse
		source/destination addresses (see ethernet.Ethernet)
		"""
        self.src, self.dst = self.dst, self.src
Example #6
0
class DHCP(pypacker.Packet):
	__hdr__ = (
		("op", "B", DHCP_OP_REQUEST),
		("hrd", "B", arp.ARP_HRD_ETH),		# just like ARP.hrd
		("hln", "B", 6),			# and ARP.hln
		("hops", "B", 0),
		("xid", "I", 0xdeadbeef),
		("secs", "H", 0),
		("flags", "H", 0),
		("ciaddr", "4s", b"\x00" * 4),
		("yiaddr", "4s", b"\x00" * 4),
		("siaddr", "4s", b"\x00" * 4),
		("giaddr", "4s", b"\x00" * 4),
		# MAC + padding
		("chaddr", "16s", b"\x00" * 6 + b"\x00" * 10),
		("sname", "64s", b"\x00" * 64),
		("file", "128s", b"\x00" * 128),
		("magic", "I", DHCP_MAGIC),
		("opts", None, triggerlist.TriggerList)
	)

	ciaddr_s = pypacker.get_property_ip4("ciaddr")
	yiaddr_s = pypacker.get_property_ip4("yiaddr")
	siaddr_s = pypacker.get_property_ip4("siaddr")
	giaddr_s = pypacker.get_property_ip4("giaddr")

	def _dissect(self, buf):
		# logger.debug("DHCP: parsing options, buflen: %d" % len(buf))
		self._init_triggerlist("opts", buf[28 + 16 + 64 + 128 + 4:], DHCP.__get_opts)
		# logger.debug(buf[28+16+64+128+4:])
		# logger.debug("amount of options after parsing: %d" % len(self.opts))
		return len(buf)

	@staticmethod
	def __get_opts(buf):
		# logger.debug("DHCP: parsing options from: %s" % buf)
		opts = []
		i = 0

		while i < len(buf):
			t = buf[i]
			p = None
			# logger.debug("DHCP: adding option type %d" % t)

			# last option
			if t in [0, 0xff]:
				p = DHCPOpt(type=t, len=0)
				i += 1
			else:
				dlen = buf[i + 1]
				p = DHCPOpt(type=t, len=dlen, body_bytes=buf[i + 2: i + 2 + dlen])
				i += 2 + dlen

			# logger.debug("new option: %s" % p)
			opts.append(p)

			if t == 0xff:
				if i < len(buf):
					# padding is part of the options
					opts.append(Padding(buf[i:]))
				break

		return opts
class NewProtocol(pypacker.Packet):
	"""New protocols are subclassing Packet"""

	"""
	The protocol header is basically defined by the static field
	"__hdr__" (see layer12/ethernet.Ethernet). See code documentation
	for classes "MetaPacket" and "Packet" in pypacker/pypacker.py for
	deeper information.
	"""
	__hdr__ = (
		# Simple constant fields (fixed format, not changing size),
		# marked as type field
		("type", "B", 0x12, FIELD_FLAG_IS_TYPEFIELD),
		("src", "4s", b"\xff" * 4),
		("dst", "4s", b"\xff" * 4),
		# Simple constant field, deactivatived
		# Switching between active/inactive should be avoided because of performance penalty :/
		("idk", "H", None),
		# Simple constant field, marked for auto update (see bin(...))
		("hlen", "H", 14, FIELD_FLAG_AUTOUPDATE),
		# Simple constant field
		("flags", "B", 0),
		# TriggerList field (variable length, can contain raw bytes, key/value-tuples and Packets)
		("options", None, triggerlist.TriggerList),
		# Dynamic field (bytestring format, *can* change size)
		# Field type should be avoided because of performance penalty :/
		("yolo", None, b"1234")
	)

	# Conveniant access should be enabled using properties eg using pypacker.get_property_xxx(...)
	src_s = pypacker.get_property_ip4("src")
	dst_s = pypacker.get_property_ip4("dst")
	# xxx_s = pypacker.get_property_mac("xxx")
	# xxx_s = pypacker.get_property_dnsname("xxx")

	# Setting/getting values smaller then 1 Byte should be enabled using properties (see layer3/ip.IP -> v, hl)
	def __get_flag_fluxcapacitor(self):
		return (self.flags & 0x80) >> 15

	def __set_flag_fluxcapacitor(self, value):
		value_shift = (value & 1) << 15
		self.flags = (self.flags & ~0x80) | value_shift

	flag_fluxcapacitor = property(__get_flag_fluxcapacitor, __set_flag_fluxcapacitor)

	@staticmethod
	def _parse_options(buf):
		"""Parse contents for TriggerList-field options"""
		ret = []
		off = 0

		while off < len(buf):
			ret.append(buf[off: off + 2])
			off += 2
		return ret

	def _dissect(self, buf):
		"""
		_dissect(...) must be overwritten if the header format can change
		from its original format. This is generally the case when
		- using TriggerLists
		- simple fields can get deactivated (see ethernet.Ethernet)
		- using dynamic fields

		In NewProtocol idk can get deactivated, options is a TriggerList
		and yolo is a dynamic field so _dissect(...) needs to be defined.
		"""
		# Header fields are not yet accessible in _dissect(...) so basic information
		# (type info, header length, bytes of dynamic content etc) has to be parsed manually.
		upper_layer_type = buf[0]
		total_header_length = unpack_H(buf[9: 11])[0]
		tl_bts = buf[12: total_header_length - 12]

		# self._init_triggerlist(...) should be called to initiate TriggerLists.
		# Otherwise the list will be empty.
		self._init_triggerlist("options", tl_bts, NewProtocol._parse_options)

		# self._init_handler(...) can be called to initiate the handler of the next
		# upper layer. Which handler to be called generally depends on the type information
		# found in the current layer (see layer12/ethernet.Ethernet -> type)
		self._init_handler(upper_layer_type, buf[total_header_length:])

	"""
	Handler can be registered by defining the static field dictionary
	__handler__ where the key is the value given to self._init_handler(...)
	when dissecting the value is used for creating the upper layer.
	Add the "FIELD_FLAG_IS_TYPEFIELD" to the corresponding type field in __hdr__.
	"""
	__handler__ = {0x66: ip.IP}  # just 1 handler, who needs more?

	def _update_fields(self):
		"""
		_update_fields(...) should be overwritten to update fields which depend on the state
		of the packet like lengths, checksums etc (see layer3/ip.IP -> len, sum)
		aka auto-update fields. The variable update_auto_fields indicates if any
		field should be updated in general, XXX_au_active in turn indicates
		if the field XXX should be updated (True) or not
		(see layer3/ip.IP.bin() -> len_au_active) in particular. XXX_au_active is
		available if the field has the flag "FIELD_FLAG_AUTOUPDATE" in __hdr__.
		"""
		if update_auto_fields and self._changed() and self.hlen_au_active:
			self.hlen = self.header_len

	def bin(self, update_auto_fields=True):
		"""
		bin(...)  should be overwritten to allow more complex assemblation (eg adding padding
		at the very end -> see ethernet.Ethernet)
		"""
		return pypacker.Packet.bin(self, update_auto_fields=update_auto_fields) + b"somepadding"

	def direction(self, other):
		"""
		direction(...) should be overwritten to be able to check directions to an other packet
		(see layer12/ethernet.Ethernet)
		"""
		direction = 0

		if self.src == other.src and self.dst == other.dst:
			direction |= pypacker.Packet.DIR_SAME
		if self.src == other.dst and self.dst == other.src:
			direction |= pypacker.Packet.DIR_REV

		if direction == 0:
			direction = pypacker.Packet.DIR_UNKNOWN
		return direction

	def reverse_address(self):
		"""
		reverse_address(...) should be overwritten to be able to reverse
		source/destination addresses (see ethernet.Ethernet)
		"""
		self.src, self.dst = self.dst, self.src
Example #8
0
class IP(pypacker.Packet):
    __hdr__ = (
        ("v_hl", "B", 69),  # = 0x45
        ("tos", "B", 0),
        ("len", "H", 20),
        ("id", "H", 0),
        ("off", "H", 0),
        ("ttl", "B", 64),
        ("p", "B", IP_PROTO_TCP),
        ("sum", "H", 0),
        ("src", "4s", b"\x00" * 4),
        ("dst", "4s", b"\x00" * 4),
        ("opts", None, triggerlist.TriggerList))

    def __get_v(self):
        return self.v_hl >> 4

    def __set_v(self, value):
        self.v_hl = (value << 4) | (self.v_hl & 0xf)

    v = property(__get_v, __set_v)

    def __get_hl(self):
        return self.v_hl & 0x0f

    def __set_hl(self, value):
        self.v_hl = (self.v_hl & 0xf0) | value

    hl = property(__get_hl, __set_hl)

    # Convenient access for: src[_s], dst[_s]
    src_s = pypacker.get_property_ip4("src")
    dst_s = pypacker.get_property_ip4("dst")

    def _dissect(self, buf):
        total_header_length = ((buf[0] & 0xf) << 2)
        options_length = total_header_length - 20  # total IHL - standard IP-len = options length

        if options_length < 0:
            # invalid header length: assume no options at all
            raise Exception("invalid header length: %d" % options_length)
        elif options_length > 0:
            # logger.debug("got some IP options: %s" % tl_opts)
            self._init_triggerlist("opts", buf[20:20 + options_length],
                                   self.__parse_opts)

        self._init_handler(buf[9], buf[total_header_length:])
        return total_header_length

    __IP_OPT_SINGLE = set([IP_OPT_EOOL, IP_OPT_NOP])

    @staticmethod
    def __parse_opts(buf):
        """Parse IP options and return them as List."""
        optlist = []
        i = 0
        p = None

        while i < len(buf):
            # logger.debug("got IP-option type %s" % buf[i])
            if buf[i] in IP.__IP_OPT_SINGLE:
                p = IPOptSingle(type=buf[i])
                i += 1
            else:
                olen = buf[i + 1]
                # logger.debug("IPOptMulti")
                p = IPOptMulti(type=buf[i],
                               len=olen,
                               body_bytes=buf[i + 2:i + olen])
                # logger.debug("body bytes: %s" % buf[i + 2: i + olen])
                i += olen  # typefield + lenfield + data-len
                # logger.debug("IPOptMulti 2")
            optlist.append(p)
        return optlist

    def bin(self, update_auto_fields=True):
        if update_auto_fields:
            if self._changed():
                self.len = len(self)
                # length changed so we have to recalculate checksum
                # logger.debug("updating checksum")
                # logger.debug(">>> IP: calculating sum")
                # reset checksum for recalculation,  mark as changed / clear cache
                self.sum = 0
                # Update header length. NOTE: needs to be a multiple of 4 Bytes.
                # logger.debug("updating: %r" % self._packet)
                # options length need to be multiple of 4 Bytes
                self._hl = int(self.header_len / 4) & 0xf
                # logger.debug(">>> IP: bytes for sum: %s" % self.header_bytes)
                self.sum = in_cksum(self._pack_header())
                # logger.debug("IP: new hl: %d / %d" % (self._packet.hdr_len, hdr_len_off))
                # logger.debug("new sum: %0X" % self.sum)
                # logger.debug("new sum: %d" % self.sum)

        return pypacker.Packet.bin(self, update_auto_fields=update_auto_fields)

    def direction(self, other):
        # logger.debug("checking direction: %s<->%s" % (self, next))
        # TODO: handle broadcast
        if self.src == other.src and self.dst == other.dst:
            # consider packet to itself: can be DIR_REV
            return pypacker.Packet.DIR_SAME | pypacker.Packet.DIR_REV
        elif self.src == other.dst and self.dst == other.src:
            return pypacker.Packet.DIR_REV
        else:
            return pypacker.Packet.DIR_UNKNOWN

    def reverse_address(self):
        self.src, self.dst = self.dst, self.src