Allow filters in the Packet slice / .getlayer() API

With a packet `pkt = IP() / IP(ttl=3) / IP()`, this will allow:

  - pkt.getlayer(IP, ttl=3)
  - pkt[IP, {"ttl": 3}]

This is particularly useful with Dot11Elt() layers, when you want a
specific value (the SSID or supported rates for example).
This commit is contained in:
Pierre LALET 2017-09-25 11:07:38 +02:00
parent 6e173e9f6e
commit 8ac38c611c
6 changed files with 48 additions and 44 deletions

View File

@ -619,16 +619,15 @@ class BGPCapability(six.with_metaclass(_BGPCapability_metaclass, Packet)):
ret = 1
return ret
def getlayer(self, cls, nb=1, _track=None):
layer = None
def getlayer(self, cls, nb=1, _track=None, **flt):
if cls == BGPCapability:
for cap_class in _capabilities_registry:
if isinstance(self, _capabilities_registry[cap_class]):
layer = self
break
if isinstance(self, _capabilities_registry[cap_class]) and \
all(self.getfieldval(fldname) == fldvalue
for fldname, fldvalue in flt.iteritems()):
return self
else:
layer = Packet.getlayer(self, cls, nb, _track)
return layer
return Packet.getlayer(self, cls, nb, _track, **flt)
def post_build(self, p, pay):
length = 0

View File

@ -248,16 +248,15 @@ class EAP(Packet):
ret = 1
return ret
def getlayer(self, cls, nb=1, _track=None):
layer = None
def getlayer(self, cls, nb=1, _track=None, **flt):
if cls == EAP:
for eap_class in EAP.registered_methods.values():
if isinstance(self, eap_class):
layer = self
break
if isinstance(self, eap_class) and \
all(self.getfieldval(fldname) == fldvalue
for fldname, fldvalue in flt.iteritems()):
return self
else:
layer = Packet.getlayer(self, cls, nb, _track)
return layer
return Packet.getlayer(self, cls, nb, _track, **flt)
def answers(self, other):
if isinstance(other, EAP):

View File

@ -255,21 +255,20 @@ class NTP(Packet):
ret = 1
return ret
def getlayer(self, cls, nb=1, _track=None):
def getlayer(self, cls, nb=1, _track=None, **flt):
ntp_classes = [
get_cls("NTPHeader"),
get_cls("NTPControl"),
get_cls("NTPPrivate")
]
layer = None
if cls == NTP:
for ntp_class in ntp_classes:
if isinstance(self, ntp_class):
layer = self
break
if isinstance(self, ntp_class) and \
all(self.getfieldval(fldname) == fldvalue
for fldname, fldvalue in flt.iteritems()):
return self
else:
layer = Packet.getlayer(self, cls, nb, _track)
return layer
return Packet.getlayer(self, cls, nb, _track, **flt)
def mysummary(self):
return self.sprintf("NTP v%ir,NTP.version%, %NTP.mode%")

View File

@ -278,17 +278,15 @@ class RadiusAttribute(Packet):
return True
return False
def getlayer(self, cls, nb=1, _track=None):
layer = None
def getlayer(self, cls, nb=1, _track=None, **flt):
if cls == RadiusAttribute:
for attr_class in RadiusAttribute.registered_attributes.values():
if isinstance(self, attr_class):
layer = self
break
if isinstance(self, attr_class) and \
all(self.getfieldval(fldname) == fldvalue
for fldname, fldvalue in flt.iteritems()):
return self
else:
layer = Packet.getlayer(self, cls, nb, _track)
return layer
return Packet.getlayer(self, cls, nb, _track, **flt)
def post_build(self, p, pay):
length = self.len

View File

@ -888,8 +888,12 @@ class Packet(six.with_metaclass(Packet_metaclass, BasePacket)):
if ret:
return ret
return self.payload.haslayer(cls)
def getlayer(self, cls, nb=1, _track=None):
"""Return the nb^th layer that is an instance of cls."""
def getlayer(self, cls, nb=1, _track=None, **flt):
"""Return the nb^th layer that is an instance of cls, matching flt
values.
"""
if isinstance(cls, int):
nb = cls+1
cls = None
@ -898,13 +902,15 @@ class Packet(six.with_metaclass(Packet_metaclass, BasePacket)):
else:
ccls,fld = cls,None
if cls is None or self.__class__ == cls or self.__class__.__name__ == ccls:
if nb == 1:
if fld is None:
return self
if all(self.getfieldval(fldname) == fldvalue
for fldname, fldvalue in flt.iteritems()):
if nb == 1:
if fld is None:
return self
else:
return self.getfieldval(fld)
else:
return self.getfieldval(fld)
else:
nb -=1
nb -=1
for f in self.packetfields:
fvalue_gen = self.getfieldval(f.name)
if fvalue_gen is None:
@ -918,7 +924,7 @@ class Packet(six.with_metaclass(Packet_metaclass, BasePacket)):
if ret is not None:
return ret
nb = track[0]
return self.payload.getlayer(cls,nb,_track=_track)
return self.payload.getlayer(cls, nb=nb, _track=_track, **flt)
def firstlayer(self):
q = self
@ -930,13 +936,11 @@ class Packet(six.with_metaclass(Packet_metaclass, BasePacket)):
if isinstance(cls, slice):
lname = cls.start
if cls.stop:
ret = self.getlayer(cls.start, cls.stop)
ret = self.getlayer(cls.start, nb=cls.stop, **(cls.step or {}))
else:
ret = self.getlayer(cls.start)
if ret is None and cls.step is not None:
ret = cls.step
ret = self.getlayer(cls.start, **(cls.step or {}))
else:
lname=cls
lname = cls
ret = self.getlayer(cls)
if ret is None:
if isinstance(lname, Packet_metaclass):
@ -1287,7 +1291,7 @@ class NoPayload(Packet):
return isinstance(other, NoPayload) or isinstance(other, conf.padding_layer)
def haslayer(self, cls):
return 0
def getlayer(self, cls, nb=1, _track=None):
def getlayer(self, cls, nb=1, _track=None, **flt):
if _track is not None:
_track.append(nb)
return None

View File

@ -8645,6 +8645,11 @@ Dot11(type=0, subtype=1).answers(query) == True
assert Dot11Elt(info="scapy").summary() == "SSID='scapy'"
assert Dot11Elt(ID=1).mysummary() == ""
= Multiple Dot11Elt layers
pkt = Dot11() / Dot11Beacon() / Dot11Elt(ID="Rates") / Dot11Elt(ID="SSID", info="Scapy")
assert pkt[Dot11Elt::{"ID": 0}].info == "Scapy"
assert pkt.getlayer(Dot11Elt, ID=0).info == "Scapy"
= Dot11WEP - build
~ crypto
conf.wepkey = ""