Skip to content

Commit ae8d063

Browse files
committed
Add RoCE DETH and ImmDt sub-headers
Allow parsing unreliable datagram headers. The code adds guess_payload_class functions to find the correct sub-header according to BTH.opcode, and also automatic selection of the opcode when possible.
1 parent 6508907 commit ae8d063

File tree

2 files changed

+112
-3
lines changed

2 files changed

+112
-3
lines changed

scapy/contrib/roce.py

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,19 @@
1212

1313
from scapy.packet import Packet, bind_layers, Raw
1414
from scapy.fields import ByteEnumField, ByteField, XByteField, \
15-
ShortField, XShortField, XLongField, BitField, XBitField, FCSField
15+
ShortField, XIntField, XShortField, XLongField, BitField, \
16+
XBitField, FCSField
1617
from scapy.layers.inet import IP, UDP
1718
from scapy.layers.l2 import Ether
1819
from scapy.compat import raw
1920
from scapy.error import warning
2021
from zlib import crc32
2122
import struct
22-
from scapy.compat import Tuple
23+
from scapy.compat import (
24+
Optional,
25+
Tuple,
26+
Type,
27+
)
2328

2429
_transports = {
2530
'RC': 0x00,
@@ -28,6 +33,20 @@
2833
'UD': 0x60,
2934
}
3035

36+
OP_MASK = 0x1f
37+
TRANSPORT_MASK = 0xe0
38+
39+
40+
def transport(opcode):
41+
# type: (int) -> int
42+
return opcode & TRANSPORT_MASK
43+
44+
45+
def op(opcode):
46+
# type: (int) -> int
47+
return opcode & OP_MASK
48+
49+
3150
_ops = {
3251
'SEND_FIRST': 0x00,
3352
'SEND_MIDDLE': 0x01,
@@ -54,6 +73,8 @@
5473

5574

5675
CNP_OPCODE = 0x81
76+
UD_SEND_ONLY = _transports['UD'] | _ops['SEND_ONLY']
77+
UD_SEND_ONLY_IMM = _transports['UD'] | _ops['SEND_ONLY_WITH_IMMEDIATE']
5778

5879

5980
def opcode(transport, op):
@@ -129,7 +150,7 @@ def opcode(transport, op):
129150
class BTH(Packet):
130151
name = "BTH"
131152
fields_desc = [
132-
ByteEnumField("opcode", 0, _bth_opcodes),
153+
ByteEnumField("opcode", None, _bth_opcodes),
133154
BitField("solicited", 0, 1),
134155
BitField("migreq", 0, 1),
135156
BitField("padcount", 0, 2),
@@ -188,10 +209,34 @@ def compute_icrc(self, p):
188209
def post_build(self, p, pay):
189210
# type: (bytes, bytes) -> bytes
190211
p += pay
212+
if self.opcode is None:
213+
opcode = self.guess_opcode() or 0
214+
p = struct.pack('B', opcode) + p[1:]
191215
if self.icrc is None:
192216
p = p[:-4] + self.compute_icrc(p)
193217
return p
194218

219+
def guess_opcode(self):
220+
# type: () -> Optional[int]
221+
'Guess the opcode based on the following layers.'
222+
if isinstance(self.payload, DETH):
223+
if isinstance(self.payload.payload, ImmDt):
224+
return UD_SEND_ONLY_IMM
225+
return UD_SEND_ONLY
226+
227+
def guess_payload_class(self, payload):
228+
# type: (bytes) -> Type[Packet]
229+
opcode = self.opcode
230+
if transport(opcode) == _transports['RC'] or \
231+
transport(opcode) == _transports['UC']:
232+
cur_op = op(opcode)
233+
if cur_op in (_ops['SEND_LAST_WITH_IMMEDIATE'],
234+
_ops['SEND_ONLY_WITH_IMMEDIATE']):
235+
return ImmDt
236+
if transport(self.opcode) == _transports['UD']:
237+
return DETH
238+
return Packet.guess_payload_class(self, payload)
239+
195240

196241
class CNPPadding(Packet):
197242
name = "CNPPadding"
@@ -228,6 +273,30 @@ class AETH(Packet):
228273
]
229274

230275

276+
class DETH(Packet):
277+
name = "Datagram Extended Transport Header"
278+
fields_desc = [
279+
XIntField("qkey", 0),
280+
XByteField("reserved", 0),
281+
XBitField("sqp", 0, 24)
282+
]
283+
284+
def guess_payload_class(self, payload):
285+
# type: (bytes) -> Type[Packet]
286+
bth = self.underlayer
287+
if isinstance(bth, BTH):
288+
if bth.opcode == opcode('UD', 'SEND_ONLY_WITH_IMMEDIATE')[0]:
289+
return ImmDt
290+
return Packet.guess_payload_class(self, payload)
291+
292+
293+
class ImmDt(Packet):
294+
name = "Immediate Data Extended Transport Header"
295+
fields_desc = [
296+
XIntField("imm", 0)
297+
]
298+
299+
231300
bind_layers(BTH, CNPPadding, opcode=CNP_OPCODE)
232301

233302
bind_layers(Ether, GRH, type=0x8915)

test/contrib/roce.uts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,43 @@ assert not pkt[BTH].ackreq
124124
assert pkt[AETH].syndrome == 0
125125
assert pkt[AETH].msn == 5
126126
assert pkt.icrc == 0x25f0c038
127+
128+
= UD with / w.o immediate
129+
130+
payload = b'test payload'
131+
132+
pkt = (Ether()/
133+
IP()/
134+
UDP(sport=5555)/
135+
BTH()/
136+
DETH(sqp=1234)/
137+
Raw(payload)
138+
)
139+
140+
dissected = Ether(pkt.build() + b'\x00' * 4)
141+
dissected.show2()
142+
143+
assert BTH in dissected.layers()
144+
assert pkt.opcode is None
145+
assert BTH.opcode.i2repr(dissected[BTH], dissected.opcode) == 'UD_SEND_ONLY'
146+
assert DETH in dissected.layers()
147+
assert dissected.sqp == 1234
148+
149+
imm_value = 123456789
150+
151+
pkt = (Ether()/
152+
IP()/
153+
UDP(sport=5555)/
154+
BTH()/
155+
DETH()/
156+
ImmDt(imm=imm_value)/
157+
Raw(payload)
158+
)
159+
160+
dissected = Ether(pkt.build() + b'\x00' * 4)
161+
assert BTH in dissected.layers()
162+
assert pkt.opcode is None
163+
assert BTH.opcode.i2repr(dissected[BTH], dissected.opcode) == 'UD_SEND_ONLY_WITH_IMMEDIATE'
164+
assert DETH in dissected.layers()
165+
assert ImmDt in dissected.layers()
166+
assert pkt.imm == imm_value

0 commit comments

Comments
 (0)