Skip to content

Commit 670abbd

Browse files
authored
Merge pull request #2733 from gpotter2/dot11-ie
802.11 IEs + BitField complex LE support
2 parents 880756c + 63b9afb commit 670abbd

6 files changed

Lines changed: 534 additions & 137 deletions

File tree

scapy/fields.py

Lines changed: 114 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,32 +1596,73 @@ def m2i(self, pkt, x):
15961596

15971597

15981598
class BitField(Field):
1599-
__slots__ = ["rev", "size"]
1599+
"""
1600+
Field to handle bits.
1601+
1602+
:param name: name of the field
1603+
:param default: default value
1604+
:param size: size (in bits). If negative, Low endian
1605+
:param tot_size: size of the total group of bits (in bytes) the bitfield
1606+
is in. If negative, Low endian.
1607+
:param end_tot_size: same but for the BitField ending a group.
1608+
1609+
Example - normal usage::
1610+
1611+
0 1 2 3
1612+
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1613+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1614+
| A | B | C |
1615+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1616+
1617+
Fig. TestPacket
1618+
1619+
class TestPacket(Packet):
1620+
fields_desc = [
1621+
BitField("a", 0, 14),
1622+
BitField("b", 0, 16),
1623+
BitField("c", 0, 2),
1624+
]
1625+
1626+
Example - Low endian stored as 16 bits on the network::
1627+
1628+
x x x x x x x x x x x x x x x x
1629+
a [b] [ c ] [ a ]
1630+
1631+
Will first get reversed during dissecion:
1632+
1633+
x x x x x x x x x x x x x x x x
1634+
[ a ] [b] [ c ]
1635+
1636+
class TestPacket(Packet):
1637+
fields_desc = [
1638+
BitField("a", 0, 9, tot_size=-16),
1639+
BitField("b", 0, 2),
1640+
BitField("c", 0, 5, end_tot_size=-16)
1641+
]
1642+
1643+
"""
1644+
__slots__ = ["rev", "size", "tot_size", "end_tot_size"]
16001645

1601-
def __init__(self, name, default, size):
1646+
def __init__(self, name, default, size,
1647+
tot_size=0, end_tot_size=0):
16021648
Field.__init__(self, name, default)
1603-
self.rev = size < 0
1649+
self.rev = size < 0 or tot_size < 0 or end_tot_size < 0
16041650
self.size = abs(size)
1651+
if not tot_size:
1652+
tot_size = self.size // 8
1653+
self.tot_size = abs(tot_size)
1654+
if not end_tot_size:
1655+
end_tot_size = self.size // 8
1656+
self.end_tot_size = abs(end_tot_size)
16051657
self.sz = self.size / 8.
16061658

1607-
def reverse(self, val):
1608-
if self.size == 16:
1609-
# Replaces socket.ntohs (but work on both little/big endian)
1610-
val = struct.unpack('>H', struct.pack('<H', int(val)))[0]
1611-
elif self.size == 32:
1612-
# Same here but for socket.ntohl
1613-
val = struct.unpack('>I', struct.pack('<I', int(val)))[0]
1614-
return val
1615-
16161659
def addfield(self, pkt, s, val):
16171660
val = self.i2m(pkt, val)
16181661
if isinstance(s, tuple):
16191662
s, bitsdone, v = s
16201663
else:
16211664
bitsdone = 0
16221665
v = 0
1623-
if self.rev:
1624-
val = self.reverse(val)
16251666
v <<= self.size
16261667
v |= val & ((1 << self.size) - 1)
16271668
bitsdone += self.size
@@ -1632,13 +1673,20 @@ def addfield(self, pkt, s, val):
16321673
if bitsdone:
16331674
return s, bitsdone, v
16341675
else:
1676+
# Apply LE if necessary
1677+
if self.rev and self.end_tot_size > 1:
1678+
s = s[:-self.end_tot_size] + s[-self.end_tot_size:][::-1]
16351679
return s
16361680

16371681
def getfield(self, pkt, s):
16381682
if isinstance(s, tuple):
16391683
s, bn = s
16401684
else:
16411685
bn = 0
1686+
# Apply LE if necessary
1687+
if self.rev and self.tot_size > 1:
1688+
s = s[:self.tot_size][::-1] + s[self.tot_size:]
1689+
16421690
# we don't want to process all the string
16431691
nb_bytes = (self.size + bn - 1) // 8 + 1
16441692
w = s[:nb_bytes]
@@ -1656,9 +1704,6 @@ def getfield(self, pkt, s):
16561704
# remove low order bits
16571705
b = b >> (nb_bytes * 8 - self.size - bn)
16581706

1659-
if self.rev:
1660-
b = self.reverse(b)
1661-
16621707
bn += self.size
16631708
s = s[bn // 8:]
16641709
bn = bn % 8
@@ -2120,7 +2165,7 @@ class FlagsField(BitField):
21202165
21212166
:param name: field's name
21222167
:param default: default value for the field
2123-
:param size: number of bits in the field
2168+
:param size: number of bits in the field (in bits)
21242169
:param names: (list or dict) label for each flag, Least Significant Bit tag's name is written first # noqa: E501
21252170
"""
21262171
ismutable = True
@@ -2402,7 +2447,50 @@ def i2repr(self, pkt, x):
24022447
return "%s sec" % x
24032448

24042449

2405-
class ScalingField(Field):
2450+
class _ScalingField(object):
2451+
def __init__(self, name, default, scaling=1, unit="",
2452+
offset=0, ndigits=3, fmt="B"):
2453+
self.scaling = scaling
2454+
self.unit = unit
2455+
self.offset = offset
2456+
self.ndigits = ndigits
2457+
Field.__init__(self, name, default, fmt)
2458+
2459+
def i2m(self, pkt, x):
2460+
if x is None:
2461+
x = 0
2462+
x = (x - self.offset) / self.scaling
2463+
if isinstance(x, float) and self.fmt[-1] != "f":
2464+
x = int(round(x))
2465+
return x
2466+
2467+
def m2i(self, pkt, x):
2468+
x = x * self.scaling + self.offset
2469+
if isinstance(x, float) and self.fmt[-1] != "f":
2470+
x = round(x, self.ndigits)
2471+
return x
2472+
2473+
def any2i(self, pkt, x):
2474+
if isinstance(x, (str, bytes)):
2475+
x = struct.unpack(self.fmt, bytes_encode(x))[0]
2476+
x = self.m2i(pkt, x)
2477+
return x
2478+
2479+
def i2repr(self, pkt, x):
2480+
return "%s %s" % (self.i2h(pkt, x), self.unit)
2481+
2482+
def randval(self):
2483+
value = super(_ScalingField, self).randval()
2484+
if value is not None:
2485+
min_val = round(value.min * self.scaling + self.offset,
2486+
self.ndigits)
2487+
max_val = round(value.max * self.scaling + self.offset,
2488+
self.ndigits)
2489+
2490+
return RandFloat(min(min_val, max_val), max(min_val, max_val))
2491+
2492+
2493+
class ScalingField(_ScalingField, Field):
24062494
""" Handle physical values which are scaled and/or offset for communication
24072495
24082496
Example:
@@ -2442,48 +2530,15 @@ class ScalingField(Field):
24422530
:param ndigits: number of fractional digits for the internal conversion
24432531
:param fmt: struct.pack format used to parse and serialize the internal value from and to machine representation # noqa: E501
24442532
"""
2445-
__slots__ = ["scaling", "unit", "offset", "ndigits"]
2446-
2447-
def __init__(self, name, default, scaling=1, unit="",
2448-
offset=0, ndigits=3, fmt="B"):
2449-
self.scaling = scaling
2450-
self.unit = unit
2451-
self.offset = offset
2452-
self.ndigits = ndigits
2453-
Field.__init__(self, name, default, fmt)
2454-
2455-
def i2m(self, pkt, x):
2456-
if x is None:
2457-
x = 0
2458-
x = (x - self.offset) / self.scaling
2459-
if isinstance(x, float) and self.fmt[-1] != "f":
2460-
x = int(round(x))
2461-
return x
24622533

2463-
def m2i(self, pkt, x):
2464-
x = x * self.scaling + self.offset
2465-
if isinstance(x, float) and self.fmt[-1] != "f":
2466-
x = round(x, self.ndigits)
2467-
return x
24682534

2469-
def any2i(self, pkt, x):
2470-
if isinstance(x, (str, bytes)):
2471-
x = struct.unpack(self.fmt, bytes_encode(x))[0]
2472-
x = self.m2i(pkt, x)
2473-
return x
2474-
2475-
def i2repr(self, pkt, x):
2476-
return "%s %s" % (self.i2h(pkt, x), self.unit)
2477-
2478-
def randval(self):
2479-
value = super(ScalingField, self).randval()
2480-
if value is not None:
2481-
min_val = round(value.min * self.scaling + self.offset,
2482-
self.ndigits)
2483-
max_val = round(value.max * self.scaling + self.offset,
2484-
self.ndigits)
2485-
2486-
return RandFloat(min(min_val, max_val), max(min_val, max_val))
2535+
class BitScalingField(_ScalingField, BitField):
2536+
"""
2537+
A ScalingField that is a BitField
2538+
"""
2539+
def __init__(self, name, default, size, *args, **kwargs):
2540+
_ScalingField.__init__(self, name, default, *args, **kwargs)
2541+
BitField.__init__(self, name, default, size)
24872542

24882543

24892544
class UUIDField(Field):

scapy/layers/bluetooth.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -201,27 +201,16 @@ def mysummary(self):
201201

202202
class HCI_ACL_Hdr(Packet):
203203
name = "HCI ACL header"
204-
# NOTE: the 2-bytes entity formed by the 2 flags + handle must be LE
205-
# This means that we must reverse those two bytes manually (we don't have
206-
# a field that can reverse a group of fields)
207-
fields_desc = [BitField("BC", 0, 2), # ]
208-
BitField("PB", 0, 2), # ]=> 2 bytes
209-
BitField("handle", 0, 12), # ]
204+
fields_desc = [BitField("BC", 0, 2, tot_size=-2),
205+
BitField("PB", 0, 2),
206+
BitField("handle", 0, 12, end_tot_size=-2),
210207
LEShortField("len", None), ]
211208

212-
def pre_dissect(self, s):
213-
return s[:2][::-1] + s[2:] # Reverse the 2 first bytes
214-
215-
def post_dissect(self, s):
216-
self.raw_packet_cache = None # Reset packet to allow post_build
217-
return s
218-
219209
def post_build(self, p, pay):
220210
p += pay
221211
if self.len is None:
222212
p = p[:2] + struct.pack("<H", len(pay)) + p[4:]
223-
# Reverse, opposite of pre_dissect
224-
return p[:2][::-1] + p[2:] # Reverse (again) the 2 first bytes
213+
return p
225214

226215

227216
class L2CAP_Hdr(Packet):

0 commit comments

Comments
 (0)