Skip to content

Improve performance with nested PacketFields #4727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 32 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9db46b8
Add 'test/perf_packet_fields.uts' test (#4705)
alxroyer-thales Mar 21, 2025
4bbaab2
Rename classes in 'test/perf_packet_fields.uts' (#4705)
alxroyer-thales Mar 31, 2025
9bf0c54
Add 'test/auto_clear_cache.uts' test (#4706)
alxroyer-thales Mar 31, 2025
772459f
Improve 'perf_packet_fields.uts' (#4705)
alxroyer-thales Apr 6, 2025
cc2572d
Improve 'auto_clear_cache.uts' (#4706, #4707)
alxroyer-thales Apr 6, 2025
2d3c005
Improve 'perf_packet_fields.uts' with operation counts (#4705)
alxroyer-thales Apr 23, 2025
6e72bc3
Fix 'perf_packet_fields.uts' when `NUMBER_OF_F_PER_I>255` (#4705)
alxroyer-thales Apr 23, 2025
af50227
Report scapy@2.4.5 optimizations in scapy@2.6.1 (#4705, #4706, #4707)
alxroyer-thales Apr 3, 2025
debbbde
Avoid `add_payload()` clearing the cache when dissecting (#4706)
alxroyer-thales Apr 7, 2025
35877da
Restore default `clear_cache()` behaviour (#4707)
alxroyer-thales Apr 7, 2025
d6e0cde
Revert `_PacketField.m2i()` changes (#4705)
alxroyer-thales Apr 18, 2025
ead2e51
Revert `clear_cache()` for payload modifications (#4706)
alxroyer-thales Apr 23, 2025
e7465c8
Ensure cache set for good at the end of `Packet._fast_copy()` (#4705)
alxroyer-thales Apr 23, 2025
33aeae4
Fix parent not set when building default packet fields (#4706, #4707)
alxroyer-thales Apr 23, 2025
0976a60
Fix Thales copyrights (#4705, #4706, #4707)
alxroyer-thales Jun 27, 2025
7fed967
Fix `post_build()` not being called anymore (#4705)
alxroyer-thales Jul 16, 2025
625154c
Fix `NoPayload.clear_cache()` signature (#4707)
alxroyer-thales Jul 16, 2025
4583af8
Fix `setfieldval()` when the field is not set yet (#4705)
alxroyer-thales Jul 16, 2025
7f0f095
Add `clear_cache()` calls in 'test/fields.uts' (#4705)
alxroyer-thales Jul 18, 2025
d4d6655
Fix cache issues with packet list fields (#4705)
alxroyer-thales Jul 18, 2025
bf0fa0e
Revert "Add `clear_cache()` calls in 'test/fields.uts' (#4705)"
alxroyer-thales Jul 18, 2025
e32dab1
Avoid caching for packets returned by `fuzz()` (#4705)
alxroyer-thales Jul 18, 2025
23b1628
Adjust `MAX_TIME_PER_OP` in 'perf_packet_fields.uts' (#4705)
alxroyer-thales Jul 18, 2025
5a1f68f
Avoid `fuzz()` messing up `default_fields` (#4705)
alxroyer-thales Jul 21, 2025
471bf40
Make `FlagValue` bound to a `Packet` (#4705)
alxroyer-thales Jul 21, 2025
9415f58
Make `Packet._ensure_bound_field_value()` recursive on lists (#4705)
alxroyer-thales Jul 21, 2025
2fec703
Rework `list_field` as `ListValue` in 'fields.py' (#4705)
alxroyer-thales Jul 21, 2025
9c26a32
Fix `Packet.setfieldval()` when `attr` not already in `self.fields` (…
alxroyer-thales Jul 22, 2025
000e16b
Copy the `no_cache` flag in `Packet._fast_copy()` (#4705)
alxroyer-thales Jul 23, 2025
46f12db
Fix unit test (#4705)
alxroyer-thales Jul 23, 2025
d64e574
Avoid caching with `BTLE` layer (#4705)
alxroyer-thales Jul 23, 2025
d877990
Hide `ValueError`s in `Packet.setfieldval()` optimization (#4705)
alxroyer-thales Jul 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 75 additions & 1 deletion scapy/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1619,6 +1619,70 @@ def getfield(self,
return s[len_pkt:], i


class _ListValueMeta(type):
"""
Wraps modifying methods for ``list`` base type.

Inspired from https://stackoverflow.com/questions/8858525/track-changes-to-lists-and-dictionaries-in-python#8859168.
"""
def __new__(
mcs,
name, # type: str
bases, # Tuple[type, ...]
attrs, # type: Dict[str, Any]
): # type: (...) -> type
# List names of `list` methods modifying the list.
for method_name in [
"append",
"clear",
"extend",
"insert",
"pop",
"remove",
"reverse", # Memo: Reverse *IN PLACE*.
"sort", # Memo: Stable sort *IN PLACE*.
"__delitem__",
"__iadd__",
"__imul__",
"__setitem__",
]:
# Wrap the method so that `Packet.clear_cache()` be automatically called.
attrs[method_name] = _ListValueMeta._wrap_method(getattr(list, method_name))
return type.__new__(mcs, name, bases, attrs)

@staticmethod
def _wrap_method(meth): # type: (Callable[[Any, ...], Any]) -> Callable[[Any, ...], Any]
def wrapped(
self, # type: ListValue
*args, # type: Any
**kwargs, # type: Any
): # type: (...) -> Any
# Automatically call `Packet.clear_cache()` when the `ListValue` is modified.
self.pkt.clear_cache(upwards=True, downwards=False)

# Call the wrapped method, and return its result.
return meth(self, *args, **kwargs)
return wrapped


class ListValue(list, metaclass=_ListValueMeta):
"""
Overrides the base ``list`` type for list fields bound with packets.

Ensures ``Packet.clear_cache()`` is called when the list is modified.
"""
def __init__(
self,
pkt, # type: Packet,
*args # type: Any
): # type: (...) -> None
# Call the `list.__init__()` super constructor.
super().__init__(*args)

#: Packet bound with this list field.
self.pkt = pkt


class PacketListField(_PacketField[List[BasePacket]]):
"""PacketListField represents a list containing a series of Packet instances
that might occur right in the middle of another Packet field.
Expand Down Expand Up @@ -2973,7 +3037,7 @@ def __next__(self):


class FlagValue(object):
__slots__ = ["value", "names", "multi"]
__slots__ = ["pkt", "value", "names", "multi"]

def _fixvalue(self, value):
# type: (Any) -> int
Expand All @@ -2990,6 +3054,12 @@ def _fixvalue(self, value):

def __init__(self, value, names):
# type: (Union[List[str], int, str], Union[List[str], str]) -> None

#: Packet bound with this flag value.
#:
#: Ensures ``Packet.clear_cache()`` is called when the flags are modified.
self.pkt = None # type: Optional[Packet]

self.multi = isinstance(names, list)
self.names = names
self.value = self._fixvalue(value)
Expand Down Expand Up @@ -3125,6 +3195,10 @@ def __setattr__(self, attr, value):
self.value |= (2 ** self.names.index(attr))
else:
self.value &= ~(2 ** self.names.index(attr))

# Automatically call `Packet.clear_cache()` when the flags are modified.
if self.pkt is not None:
self.pkt.clear_cache(upwards=True, downwards=True)
else:
return super(FlagValue, self).__setattr__(attr, value)

Expand Down
3 changes: 3 additions & 0 deletions scapy/layers/bluetooth4LE.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ class BTLE(Packet):
X3BytesField("crc", None)
]

# Avoid caching with this layer for data reordering to work properly.
no_cache = True

@staticmethod
def compute_crc(pdu, init=0x555555):
def swapbits(a):
Expand Down
Loading